triangle_mesh.template.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2021 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26#ifndef OOMPH_TRIANGLE_MESH_TEMPLATE_CC
27#define OOMPH_TRIANGLE_MESH_TEMPLATE_CC
28
29#include <iostream>
30
32#include "../generic/map_matrix.h"
33#include "../generic/multi_domain.h"
34#include "../generic/projection.h"
35#include "../generic/face_element_as_geometric_object.h"
36
37namespace oomph
38{
39 //======================================================================
40 /// Build with the help of the scaffold mesh coming
41 /// from the triangle mesh generator Triangle.
42 //======================================================================
43 template<class ELEMENT>
45 const bool& use_attributes)
46 {
47 // Mesh can only be built with 2D Telements.
48 MeshChecker::assert_geometric_element<TElementGeometricBase, ELEMENT>(2);
49
50 // Create space for elements
51 unsigned nelem = Tmp_mesh_pt->nelement();
52 Element_pt.resize(nelem);
53
54 // Create space for nodes
55 unsigned nnode_scaffold = Tmp_mesh_pt->nnode();
56
57 // Create a map storing the node_id of the mesh used to update the
58 // node position in the update_triangulateio function
59 std::map<Node*, unsigned> old_global_number;
60
61 // Store the TriangulateIO node id
62 for (unsigned inod = 0; inod < nnode_scaffold; inod++)
63 {
64 Node* old_node_pt = Tmp_mesh_pt->node_pt(inod);
65 old_global_number[old_node_pt] = inod;
66 }
67
68 // Initialize the old node id vector
69 Oomph_vertex_nodes_id.resize(nnode_scaffold);
70
71 // Create space for nodes
72 Node_pt.resize(nnode_scaffold, 0);
73
74 // Set number of boundaries
75 unsigned nbound = Tmp_mesh_pt->nboundary();
76
77 // Resize the boundary information
78 set_nboundary(nbound);
79 Boundary_element_pt.resize(nbound);
80 Face_index_at_boundary.resize(nbound);
81
82 // If we have different regions, then resize the region
83 // information
84 if (use_attributes)
85 {
86 Boundary_region_element_pt.resize(nbound);
87 Face_index_region_at_boundary.resize(nbound);
88 }
89
90 // Loop over elements in scaffold mesh, visit their nodes
91 for (unsigned e = 0; e < nelem; e++)
92 {
93 Element_pt[e] = new ELEMENT;
94 }
95
96 // Number of nodes per element from the scaffold mesh
97 unsigned nnod_el = Tmp_mesh_pt->finite_element_pt(0)->nnode();
98
99 // Setup map to check the (pseudo-)global node number
100 // Nodes whose number is zero haven't been copied across
101 // into the mesh yet.
102 std::map<Node*, unsigned> global_number;
103 unsigned global_count = 0;
104
105 // Map of Element attribute pairs
106 std::map<double, Vector<FiniteElement*>> element_attribute_map;
107
108 // If we're using attributes
109 if (use_attributes)
110 {
111 // If we're using attributes then we need attribute 0 which will
112 // be associated with region 0
113 element_attribute_map[0].resize(0);
114 }
115
116 // Loop over elements in scaffold mesh, visit their nodes
117 for (unsigned e = 0; e < nelem; e++)
118 {
119 // Loop over all nodes in element
120 for (unsigned j = 0; j < nnod_el; j++)
121 {
122 // Pointer to node in the scaffold mesh
123 Node* scaffold_node_pt = Tmp_mesh_pt->finite_element_pt(e)->node_pt(j);
124
125 // Get the (pseudo-)global node number in scaffold mesh
126 // (It's zero [=default] if not visited this one yet)
127 unsigned j_global = global_number[scaffold_node_pt];
128
129 // Haven't done this one yet
130 if (j_global == 0)
131 {
132 // Find and store the node_id in the old nodes map
133 Oomph_vertex_nodes_id[global_count] =
134 old_global_number[scaffold_node_pt];
135
136 // Get pointer to set of mesh boundaries that this
137 // scaffold node occupies; NULL if the node is not on any boundary
138 std::set<unsigned>* boundaries_pt;
139 scaffold_node_pt->get_boundaries_pt(boundaries_pt);
140
141 // Storage for the new node
142 Node* new_node_pt = 0;
143
144 // Is it on boundaries
145 if (boundaries_pt != 0)
146 {
147 // Create new boundary node
148 new_node_pt =
149 finite_element_pt(e)->construct_boundary_node(j, time_stepper_pt);
150
151 // Add to boundaries
152 for (std::set<unsigned>::iterator it = boundaries_pt->begin();
153 it != boundaries_pt->end();
154 ++it)
155 {
156 add_boundary_node(*it, new_node_pt);
157 }
158 }
159 // Build normal node
160 else
161 {
162 // Create new normal node
163 new_node_pt =
164 finite_element_pt(e)->construct_node(j, time_stepper_pt);
165 }
166
167 // Give it a number (not necessarily the global node
168 // number in the scaffold mesh -- we just need something
169 // to keep track...)
170 global_count++;
171 global_number[scaffold_node_pt] = global_count;
172
173 // Copy new node, created using the NEW element's construct_node
174 // function into global storage, using the same global
175 // node number that we've just associated with the
176 // corresponding node in the scaffold mesh
177 Node_pt[global_count - 1] = new_node_pt;
178
179 // Assign coordinates
180 for (unsigned i = 0; i < finite_element_pt(e)->dim(); i++)
181 {
182 new_node_pt->x(i) = scaffold_node_pt->x(i);
183 }
184 }
185 // This one has already been done: Copy accross
186 else
187 {
188 finite_element_pt(e)->node_pt(j) = Node_pt[j_global - 1];
189 }
190 }
191
192 // If we're using attributes
193 if (use_attributes)
194 {
195 element_attribute_map[Tmp_mesh_pt->element_attribute(e)].push_back(
196 finite_element_pt(e));
197 }
198 }
199
200 // Now let's construct lists
201 // Find the number of attributes
202 if (use_attributes)
203 {
204 unsigned n_attribute = element_attribute_map.size();
205
206 // There are n_attribute different regions
207 this->Region_attribute.resize(n_attribute);
208
209 // Copy the vectors in the map over to our internal storage
210 unsigned count = 0;
211 for (std::map<double, Vector<FiniteElement*>>::iterator it =
212 element_attribute_map.begin();
213 it != element_attribute_map.end();
214 ++it)
215 {
216 this->Region_attribute[count] = it->first;
217 Region_element_pt[static_cast<unsigned>(Region_attribute[count])] =
218 it->second;
219 ++count;
220 }
221 }
222
223 // At this point we've created all the elements and
224 // created their vertex nodes. Now we need to create
225 // the additional (midside and internal) nodes!
226
227 unsigned boundary_id = 0;
228
229 // Get number of nodes along element edge and dimension of element (2)
230 // from first element
231 unsigned n_node_1d = finite_element_pt(0)->nnode_1d();
232 unsigned dim = finite_element_pt(0)->dim();
233
234 // Storage for the local coordinate of the new node
235 Vector<double> s(dim);
236
237 // Get number of nodes in the element from first element
238 unsigned n_node = finite_element_pt(0)->nnode();
239
240 // Storage for each global edge of the mesh
241 unsigned n_global_edge = Tmp_mesh_pt->nglobal_edge();
242 Vector<Vector<Node*>> nodes_on_global_edge(n_global_edge);
243
244 // Loop over elements
245 for (unsigned e = 0; e < nelem; e++)
246 {
247 // Cache pointers to the elements
248 FiniteElement* const elem_pt = finite_element_pt(e);
249 FiniteElement* const tmp_elem_pt = Tmp_mesh_pt->finite_element_pt(e);
250
251 // The number of edge nodes is 3*(nnode_1d-1)
252 unsigned n_edge_node = 3 * (n_node_1d - 1);
253
254 // If there are any more nodes, these are internal and can be
255 // constructed and added directly to the mesh
256 for (unsigned n = n_edge_node; n < n_node; ++n)
257 {
258 // Create new node (it can never be a boundary node)
259 Node* new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
260
261 // What are the node's local coordinates?
262 elem_pt->local_coordinate_of_node(n, s);
263
264 // Find the coordinates of the new node from the existing
265 // and fully-functional element in the scaffold mesh
266 for (unsigned i = 0; i < dim; i++)
267 {
268 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
269 }
270
271 // Add the node to the mesh's global look-up scheme
272 Node_pt.push_back(new_node_pt);
273 }
274
275 // Now loop over the mid-side edge nodes
276 // Start from node number 3
277 unsigned n = 3;
278
279 // Loop over edges
280 for (unsigned j = 0; j < 3; j++)
281 {
282 // Find the boundary id of the edge
283 boundary_id = Tmp_mesh_pt->edge_boundary(e, j);
284
285 // Find the global edge index
286 unsigned edge_index = Tmp_mesh_pt->edge_index(e, j);
287
288 // If the nodes on the edge have not been allocated, construct them
289 if (nodes_on_global_edge[edge_index].size() == 0)
290 {
291 // Loop over the nodes on the edge excluding the ends
292 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
293 {
294 // Storage for the new node
295 Node* new_node_pt = 0;
296
297 // If the edge is on a boundary, construct a boundary node
298 if (boundary_id > 0)
299 {
300 new_node_pt =
301 elem_pt->construct_boundary_node(n, time_stepper_pt);
302 // Add it to the boundary
303 this->add_boundary_node(boundary_id - 1, new_node_pt);
304 }
305 // Otherwise construct a normal node
306 else
307 {
308 new_node_pt = elem_pt->construct_node(n, time_stepper_pt);
309 }
310
311 // What are the node's local coordinates?
312 elem_pt->local_coordinate_of_node(n, s);
313
314 // Find the coordinates of the new node from the existing
315 // and fully-functional element in the scaffold mesh
316 for (unsigned i = 0; i < dim; i++)
317 {
318 new_node_pt->x(i) = tmp_elem_pt->interpolated_x(s, i);
319 }
320
321 // Add to the global node list
322 Node_pt.push_back(new_node_pt);
323
324 // Add to the edge index
325 nodes_on_global_edge[edge_index].push_back(new_node_pt);
326 // Increment the node number
327 ++n;
328 }
329 }
330 // Otherwise just set the pointers
331 // using the fact that the next time the edge is visited
332 // the nodes must be arranged in the other order because all
333 // triangles have the same orientation
334 else
335 {
336 // Loop over the nodes on the edge excluding the ends
337 for (unsigned j2 = 0; j2 < n_node_1d - 2; ++j2)
338 {
339 // Set the local node from the edge but indexed the other
340 // way around
341 elem_pt->node_pt(n) =
342 nodes_on_global_edge[edge_index][n_node_1d - 3 - j2];
343 ++n;
344 }
345 }
346
347 // Set the elements adjacent to the boundary from the
348 // boundary id information
349 if (boundary_id > 0)
350 {
351 Boundary_element_pt[boundary_id - 1].push_back(elem_pt);
352 // Need to put a shift in here because of an inconsistent naming
353 // convention between triangle and face elements
354 Face_index_at_boundary[boundary_id - 1].push_back((j + 2) % 3);
355
356 // If using regions set up the boundary information
357 if (use_attributes)
358 {
359 unsigned tmp_region =
360 static_cast<unsigned>(Tmp_mesh_pt->element_attribute(e));
361 // Element adjacent to boundary
362 Boundary_region_element_pt[boundary_id - 1][tmp_region].push_back(
363 elem_pt);
364 // Need to put a shift in here because of an inconsistent naming
365 // convention between triangle and face elements
366 Face_index_region_at_boundary[boundary_id - 1][tmp_region]
367 .push_back((j + 2) % 3);
368 }
369 }
370
371 } // end of loop over edges
372 } // end of loop over elements
373
374
375 // Lookup scheme has now been setup
376 Lookup_for_elements_next_boundary_is_setup = true;
377 }
378
379#ifdef OOMPH_HAS_MPI
380
381 //======================================================================
382 /// Identify the segments from the old mesh (original mesh)
383 /// in the new mesh (this) and assign initial and final boundary
384 /// coordinates for the segments that create the boundary
385 //======================================================================
386 template<class ELEMENT>
389 const unsigned& b, TriangleMesh<ELEMENT>* original_mesh_pt)
390 {
391 // ------------------------------------------------------------------
392 // First: Get the face elements associated with the current boundary
393 // (nonhalo elements only)
394 // ------------------------------------------------------------------
395 // Temporary storage for face elements
396 Vector<FiniteElement*> face_el_pt;
397
398 // Temporary storage for number of elements adjacent to the boundary
399 unsigned nele = 0;
400
401 // Temporary storage for elements adjacent to the boundary that have
402 // a common edge (related with internal boundaries)
403 unsigned n_repeated_ele = 0;
404
405 const unsigned n_regions = this->nregion();
406
407 // map to associate the face element to the bulk element, necessary
408 // to attach halo face elements at both sides of each found segment
409 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
410
411 // Temporary storage for already done nodes
412 Vector<std::pair<Node*, Node*>> done_nodes_pt;
413
414 // If there is more than one region then only use boundary
415 // coordinates from the bulk side (region 0)
416 if (n_regions > 1)
417 {
418 for (unsigned rr = 0; rr < n_regions; rr++)
419 {
420 const unsigned region_id =
421 static_cast<unsigned>(this->Region_attribute[rr]);
422
423 // Loop over all elements on boundaries in region i_r
424 const unsigned nel_in_region =
425 this->nboundary_element_in_region(b, region_id);
426
427 unsigned nel_repetead_in_region = 0;
428
429 // Only bother to do anything else, if there are elements
430 // associated with the boundary and the current region
431 if (nel_in_region > 0)
432 {
433 // Flag that activates when a repeated face element is found,
434 // possibly because we are dealing with an internal boundary
435 bool repeated = false;
436
437 // Loop over the bulk elements adjacent to boundary b
438 for (unsigned e = 0; e < nel_in_region; e++)
439 {
440 // Get pointer to the bulk element that is adjacent to boundary b
441 FiniteElement* bulk_elem_pt =
442 this->boundary_element_in_region_pt(b, region_id, e);
443
444#ifdef OOMPH_HAS_MPI
445 // In a distributed mesh only work with nonhalo elements
446 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
447 {
448 // Increase the number of repeated elements
449 n_repeated_ele++;
450 // Go for the next element
451 continue;
452 }
453#endif
454
455 // Find the index of the face of element e along boundary b
456 int face_index =
457 this->face_index_at_boundary_in_region(b, region_id, e);
458
459 // Before adding the new element we need to be sure that
460 // the edge that this element represent has not been
461 // already added
462 FiniteElement* tmp_ele_pt =
463 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
464
465 const unsigned n_nodes = tmp_ele_pt->nnode();
466
467 std::pair<Node*, Node*> tmp_pair = std::make_pair(
468 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
469
470 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
471 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
472
473 // Search for repeated nodes
474 const unsigned n_done_nodes = done_nodes_pt.size();
475 for (unsigned l = 0; l < n_done_nodes; l++)
476 {
477 if (tmp_pair == done_nodes_pt[l] ||
478 tmp_pair_inverse == done_nodes_pt[l])
479 {
480 nel_repetead_in_region++;
481 repeated = true;
482 break;
483 }
484 }
485
486 // Create new face element
487 if (!repeated)
488 {
489 // Add the pair of nodes (edge) to the node dones
490 done_nodes_pt.push_back(tmp_pair);
491 // Create the map to know if the element is halo
492 face_el_pt.push_back(tmp_ele_pt);
493 // Add the element to the face elements
494 face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
495 }
496 else
497 {
498 // Clean up
499 delete tmp_ele_pt;
500 tmp_ele_pt = 0;
501 }
502
503 // Re-start
504 repeated = false;
505
506 } // for (e < nel_in_region)
507
508 nele += nel_in_region;
509
510 n_repeated_ele += nel_repetead_in_region;
511
512 } // if (nel_in_region > 0)
513 } // for (rr < n_regions)
514 } // if (n_regions > 1)
515 // Otherwise it's just the normal boundary functions
516 else
517 {
518 // Loop over all elements on boundaries
519 nele = this->nboundary_element(b);
520
521 // Only bother to do anything else, if there are elements
522 if (nele > 0)
523 {
524 // Flag that activates when a repeated face element is found,
525 // possibly because we are dealing with an internal boundary
526 bool repeated = false;
527
528 // Loop over the bulk elements adjacent to boundary b
529 for (unsigned e = 0; e < nele; e++)
530 {
531 // Get pointer to the bulk element that is adjacent to boundary b
532 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
533
534#ifdef OOMPH_HAS_MPI
535 // In a distributed mesh only work with nonhalo elements
536 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
537 {
538 // Increase the number of repeated elements
539 n_repeated_ele++;
540 // Go for the next element
541 continue;
542 }
543#endif
544
545 // Find the index of the face of element e along boundary b
546 int face_index = this->face_index_at_boundary(b, e);
547
548 // Before adding the new element we need to be sure that
549 // the edge that this element represents has not been
550 // already added (only applies for internal boundaries)
551 FiniteElement* tmp_ele_pt =
552 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
553
554 const unsigned n_nodes = tmp_ele_pt->nnode();
555
556 std::pair<Node*, Node*> tmp_pair = std::make_pair(
557 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
558
559 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
560 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
561
562 // Search for repeated nodes
563 const unsigned n_done_nodes = done_nodes_pt.size();
564 for (unsigned l = 0; l < n_done_nodes; l++)
565 {
566 if (tmp_pair == done_nodes_pt[l] ||
567 tmp_pair_inverse == done_nodes_pt[l])
568 {
569 // Increase the number of repeated elements
570 n_repeated_ele++;
571 // Mark the element as repeated
572 repeated = true;
573 break;
574 }
575 }
576
577 // Create new face element
578 if (!repeated)
579 {
580 // Add the pair of nodes (edge) to the node dones
581 done_nodes_pt.push_back(tmp_pair);
582 // Add the element to the face elements
583 face_el_pt.push_back(tmp_ele_pt);
584 // Create the map to know if the element is halo
585 face_to_bulk_element_pt[tmp_ele_pt] = bulk_elem_pt;
586 }
587 else
588 {
589 // Free the repeated bulk element!!
590 delete tmp_ele_pt;
591 tmp_ele_pt = 0;
592 }
593
594 // Re-start
595 repeated = false;
596
597 } // for (e < nel)
598 } // if (nel > 0)
599
600 } // else (n_regions > 1)
601
602 // Do not consider the repeated elements
603 nele -= n_repeated_ele;
604
605#ifdef PARANOID
606 if (nele != face_el_pt.size())
607 {
608 std::ostringstream error_message;
609 error_message
610 << "The independent counting of face elements (" << nele << ") for "
611 << "boundary (" << b << ") is different\n"
612 << "from the real number of face elements in the container ("
613 << face_el_pt.size() << ")\n";
614 throw OomphLibError(error_message.str(),
615 "TriangleMesh::identify_boundary_segments_and_assign_"
616 "initial_zeta_values()",
617 OOMPH_EXCEPTION_LOCATION);
618 }
619#endif
620
621 // Continue even thought there are no elements, the processor needs
622 // to participate in the communications
623
624 // ----------------------------------------------------------------
625 // Second: Sort the face elements, only consider nonhalo elements
626 // ----------------------------------------------------------------
627
628 // A flag vector to mark those face elements that are considered as
629 // halo in the current processor
630 std::vector<bool> is_halo_face_element(nele, false);
631
632 // Count the total number of non halo face elements
633 unsigned nnon_halo_face_elements = 0;
634
635 // We will have halo face elements if the mesh is distributed
636 for (unsigned ie = 0; ie < nele; ie++)
637 {
638 // Get the face element
639 FiniteElement* face_ele_pt = face_el_pt[ie];
640 // Get the bulk element
641 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_ele_pt];
642 // Check if the bulk element is halo
643 if (!tmp_bulk_ele_pt->is_halo())
644 {
645 is_halo_face_element[ie] = false;
646 nnon_halo_face_elements++;
647 }
648 else
649 {
650 // Mark the face element as halo
651 is_halo_face_element[ie] = true;
652 }
653 } // for (ie < nele)
654
655#ifdef PARANOID
656 // Get the total number of halo face elements
657 const unsigned nhalo_face_element = nele - nnon_halo_face_elements;
658 if (nhalo_face_element > 0)
659 {
660 std::ostringstream error_message;
661 error_message
662 << "There should not be halo face elements since they were not "
663 << "considered when computing the face elements\n\n"
664 << "The number of found halo face elements is: " << nhalo_face_element
665 << "\n\n";
666 throw OomphLibError(error_message.str(),
667 "TriangleMesh::identify_boundary_segments_and_assign_"
668 "initial_zeta_values()",
669 OOMPH_EXCEPTION_LOCATION);
670 }
671#endif
672
673 // The vector of list to store the "segments" that compound the
674 // boundary (segments may appear only in a distributed mesh)
675 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
676
677 // Number of already sorted face elements (only nonhalo elements for
678 // a distributed mesh)
679 unsigned nsorted_face_elements = 0;
680
681 // Keep track of who's done (this apply to nonhalo only, remember we
682 // are only working with nonhalo elements)
683 std::map<FiniteElement*, bool> done_el;
684
685 // Keep track of which element is inverted (in distributed mesh the
686 // elements may be inverted with respect to the segment they belong)
687 std::map<FiniteElement*, bool> is_inverted;
688
689 // Iterate until all possible segments have been created
690 while (nsorted_face_elements < nnon_halo_face_elements)
691 {
692 // The ordered list of face elements (in a distributed mesh a
693 // collection of contiguous face elements define a segment)
694 std::list<FiniteElement*> sorted_el_pt;
695 sorted_el_pt.clear();
696
697#ifdef PARANOID
698 // Select an initial element for the segment
699 bool found_initial_face_element = false;
700#endif
701
702 FiniteElement* ele_face_pt = 0;
703
704 unsigned iface = 0;
705 for (iface = 0; iface < nele; iface++)
706 {
707 if (!is_halo_face_element[iface])
708 {
709 ele_face_pt = face_el_pt[iface];
710 // If not done then take it as initial face element
711 if (!done_el[ele_face_pt])
712 {
713#ifdef PARANOID
714 found_initial_face_element = true;
715#endif
716 nsorted_face_elements++;
717 iface++; // The next element number
718 sorted_el_pt.push_back(ele_face_pt);
719 // Mark as done
720 done_el[ele_face_pt] = true;
721 break;
722 }
723 }
724 } // for (iface < nele)
725
726#ifdef PARANOID
727 if (!found_initial_face_element)
728 {
729 std::ostringstream error_message;
730 error_message
731 << "Could not find an initial face element for the current segment\n";
732 throw OomphLibError(error_message.str(),
733 "TriangleMesh::identify_boundary_segments_and_"
734 "assign_initial_zeta_values()",
735 OOMPH_EXCEPTION_LOCATION);
736 }
737#endif
738
739 // Number of nodes
740 const unsigned nnod = ele_face_pt->nnode();
741
742 // Left and right most nodes (the left and right nodes of the
743 // current face element)
744 Node* left_node_pt = ele_face_pt->node_pt(0);
745 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
746
747 // Continue iterating if a new face element has been added to the
748 // list
749 bool face_element_added = false;
750
751 // While a new face element has been added to the set of sorted
752 // face elements then re-iterate
753 do
754 {
755 // Start from the next face element since we have already added
756 // the previous one as the initial face element (any previous
757 // face element had to be added on previous iterations)
758 for (unsigned iiface = iface; iiface < nele; iiface++)
759 {
760 // Re-start flag
761 face_element_added = false;
762
763 // Get the candidate element
764 ele_face_pt = face_el_pt[iiface];
765
766 // Check that the candidate element has not been done and is
767 // not a halo element
768 if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
769 {
770 // Get the left and right nodes of the current element
771 Node* local_left_node_pt = ele_face_pt->node_pt(0);
772 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
773 // New element fits at the left of segment and is not inverted
774 if (left_node_pt == local_right_node_pt)
775 {
776 left_node_pt = local_left_node_pt;
777 sorted_el_pt.push_front(ele_face_pt);
778 is_inverted[ele_face_pt] = false;
779 face_element_added = true;
780 }
781 // New element fits at the left of segment and is inverted
782 else if (left_node_pt == local_left_node_pt)
783 {
784 left_node_pt = local_right_node_pt;
785 sorted_el_pt.push_front(ele_face_pt);
786 is_inverted[ele_face_pt] = true;
787 face_element_added = true;
788 }
789 // New element fits on the right of segment and is not inverted
790 else if (right_node_pt == local_left_node_pt)
791 {
792 right_node_pt = local_right_node_pt;
793 sorted_el_pt.push_back(ele_face_pt);
794 is_inverted[ele_face_pt] = false;
795 face_element_added = true;
796 }
797 // New element fits on the right of segment and is inverted
798 else if (right_node_pt == local_right_node_pt)
799 {
800 right_node_pt = local_left_node_pt;
801 sorted_el_pt.push_back(ele_face_pt);
802 is_inverted[ele_face_pt] = true;
803 face_element_added = true;
804 }
805
806 if (face_element_added)
807 {
808 done_el[ele_face_pt] = true;
809 nsorted_face_elements++;
810 break;
811 }
812
813 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
814 } // for (iiface<nnon_halo_face_element)
815 } while (face_element_added &&
816 (nsorted_face_elements < nnon_halo_face_elements));
817
818 // Store the created segment in the vector of segments
819 segment_sorted_ele_pt.push_back(sorted_el_pt);
820
821 } // while(nsorted_face_elements < nnon_halo_face_elements);
822
823 // The number of segments in this processor
824 const unsigned nsegments = segment_sorted_ele_pt.size();
825
826 // ------------------------------------------------------------------
827 // Third: We have the face elements sorted (nonhalo only), now
828 // assign boundary coordinates to the nodes in the segments. This is
829 // the LOCAL boundary coordinate which is required if the zeta
830 // values need to be inverted
831 // ------------------------------------------------------------------
832 // Necessary in case boundaries with no geom object associated need
833 // to be inverted the zeta values (It is necessary to compute the
834 // arclength but also to store the nodes in a container (set))
835 // ------------------------------------------------------------------
836
837 // Vector of sets that stores the nodes of each segment based on a
838 // lexicographically order starting from the bottom left node of
839 // each segment
840 Vector<std::set<Node*>> segment_all_nodes_pt;
841
842 // The arclength of each segment in the current processor
843 Vector<double> segment_arclength(nsegments);
844
845 // The number of vertices of each segment
846 Vector<unsigned> nvertices_per_segment(nsegments);
847
848 // The initial zeta for the segment
849 Vector<double> initial_zeta_segment(nsegments);
850
851 // The final zeta for the segment
852 Vector<double> final_zeta_segment(nsegments);
853
854#ifdef PARANOID
855 if (nnon_halo_face_elements > 0 && nsegments == 0)
856 {
857 std::ostringstream error_message;
858 error_message
859 << "The number of segments is zero, but the number of nonhalo\n"
860 << "elements is: (" << nnon_halo_face_elements << ")\n";
861 throw OomphLibError(error_message.str(),
862 "TriangleMesh::identify_boundary_segments_and_assign_"
863 "initial_zeta_values()",
864 OOMPH_EXCEPTION_LOCATION);
865 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
866#endif
867
868 // Go through all the segments and compute the LOCAL boundary
869 // coordinates
870 for (unsigned is = 0; is < nsegments; is++)
871 {
872#ifdef PARANOID
873 if (segment_sorted_ele_pt[is].size() == 0)
874 {
875 std::ostringstream error_message;
876 error_message << "The (" << is << ")-th segment has no elements\n";
877 throw OomphLibError(error_message.str(),
878 "TriangleMesh::identify_boundary_segments_and_"
879 "assign_initial_zeta_values()",
880 OOMPH_EXCEPTION_LOCATION);
881 } // if (segment_sorted_ele_pt[is].size() == 0)
882#endif
883
884 // Get access to the first element on the segment
885 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
886
887 // Number of nodes
888 const unsigned nnod = first_ele_pt->nnode();
889
890 // Get the first node of the current segment
891 Node* first_node_pt = first_ele_pt->node_pt(0);
892 if (is_inverted[first_ele_pt])
893 {
894 first_node_pt = first_ele_pt->node_pt(nnod - 1);
895 }
896
897 // Get access to the last element on the segment
898 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
899
900 // Get the last node of the current segment
901 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
902 if (is_inverted[last_ele_pt])
903 {
904 last_node_pt = last_ele_pt->node_pt(0);
905 }
906
907 // Coordinates of left node
908 double x_left = first_node_pt->x(0);
909 double y_left = first_node_pt->x(1);
910
911 // Initialise boundary coordinate (local boundary coordinate for
912 // boundaries with more than one segment)
913 Vector<double> zeta(1, 0.0);
914
915 // If the boundary has an associated GeomObject then it is not
916 // necessary to compute the arclength, only read the values from
917 // the nodes at the edges
918 if (this->boundary_geom_object_pt(b) != 0)
919 {
920 first_node_pt->get_coordinates_on_boundary(b, zeta);
921 initial_zeta_segment[is] = zeta[0];
922 last_node_pt->get_coordinates_on_boundary(b, zeta);
923 final_zeta_segment[is] = zeta[0];
924 }
925
926 // Lexicographically bottom left node
927 std::set<Node*> local_nodes_pt;
928 local_nodes_pt.insert(first_node_pt);
929
930 // Now loop over nodes in order
931 for (std::list<FiniteElement*>::iterator it =
932 segment_sorted_ele_pt[is].begin();
933 it != segment_sorted_ele_pt[is].end();
934 it++)
935 {
936 // Get element
937 FiniteElement* el_pt = *it;
938
939 // Start node and increment
940 unsigned k_nod = 1;
941 int nod_diff = 1;
942 if (is_inverted[el_pt])
943 {
944 k_nod = nnod - 2;
945 nod_diff = -1;
946 }
947
948 // Loop over nodes
949 for (unsigned j = 1; j < nnod; j++)
950 {
951 Node* nod_pt = el_pt->node_pt(k_nod);
952 k_nod += nod_diff;
953
954 // Coordinates of right node
955 double x_right = nod_pt->x(0);
956 double y_right = nod_pt->x(1);
957
958 // Increment boundary coordinate (the arclength)
959 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
960 (y_right - y_left) * (y_right - y_left));
961
962 // // When we have a GeomObject associated to the boundary we already
963 // // know the zeta values for the nodes, there is no need to compute
964 // // the arclength
965 // if (this->boundary_geom_object_pt(b)==0)
966 // {
967 // // Set boundary coordinate
968 // nod_pt->set_coordinates_on_boundary(b, zeta);
969 // }
970
971 // Increment reference coordinate
972 x_left = x_right;
973 y_left = y_right;
974
975 // Get lexicographically bottom left node but only
976 // use vertex nodes as candidates
977 local_nodes_pt.insert(nod_pt);
978 } // for (j < nnod)
979
980 } // iterator over the elements in the segment
981
982 // Store the arclength of the segment
983 segment_arclength[is] = zeta[0];
984
985 // Store the number of vertices in the segment
986 nvertices_per_segment[is] = local_nodes_pt.size();
987
988 // Add the nodes for the corresponding segment in the container
989 segment_all_nodes_pt.push_back(local_nodes_pt);
990
991 } // for (is < nsegments)
992
993 // Get the number of sets for nodes
994#ifdef PARANOID
995 if (segment_all_nodes_pt.size() != nsegments)
996 {
997 std::ostringstream error_message;
998 error_message << "The number of segments (" << nsegments
999 << ") and the number of "
1000 << "sets of nodes (" << segment_all_nodes_pt.size()
1001 << ") representing\n"
1002 << "the\nsegments is different!!!\n\n";
1003 throw OomphLibError(
1004 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1005 }
1006#endif
1007
1008 // Store the initial arclength for each segment of boundary in the
1009 // current processor, initalise to zero in case we have a non
1010 // distributed boundary
1011 Vector<double> initial_segment_arclength(nsegments, 0.0);
1012
1013 // Associated the index of the current segment to the segment index
1014 // in the original mesh (input mesh)
1015 Vector<unsigned> current_segment_to_original_segment_index(nsegments);
1016
1017 // Each segment needs to know whether it has to be inverted or not
1018 // Store whether a segment needs to be inverted or not
1019 Vector<unsigned> segment_inverted(nsegments);
1020
1021 // -----------------------------------------------------------------
1022 // Fourth: Identify the segments with the ones in the original mesh
1023 // (has sense only in the adaptation process)
1024 // -----------------------------------------------------------------
1025
1026 // Now check if there are segments associated to this boundary
1027 if (nsegments > 0)
1028 {
1029#ifdef PARANOID
1030 // Double check that the same number of coordinates (nsegments)
1031 // have been established for the boundary
1032 const unsigned nsegments_initial_coordinates =
1033 original_mesh_pt->boundary_segment_initial_coordinate(b).size();
1034
1035 const unsigned nsegments_final_coordinates =
1036 original_mesh_pt->boundary_segment_final_coordinate(b).size();
1037
1038 if (nsegments_initial_coordinates != nsegments_final_coordinates)
1039 {
1040 std::stringstream error_message;
1041 error_message
1042 << "The number of segments that present initial coordinates "
1043 << nsegments_initial_coordinates << " is different from "
1044 << "the\nnumber of segments that present final coordinates "
1045 << nsegments_final_coordinates << "\n\n";
1046 throw OomphLibError(error_message.str(),
1047 OOMPH_CURRENT_FUNCTION,
1048 OOMPH_EXCEPTION_LOCATION);
1049 } // if (nsegments_initial_coordinates!=nsegments_final_coordinates)
1050
1051 // Also check that the number of segments found in the previous
1052 // mesh is the same as the number of segments found in this mesh
1053 if (nsegments_initial_coordinates != nsegments)
1054 {
1055 std::stringstream error_message;
1056 error_message << "Working with boundary (" << b
1057 << ").\n The number of initial and "
1058 << "final coordinates (" << nsegments_initial_coordinates
1059 << ") is different from\n"
1060 << "the number of found segments (" << nsegments
1061 << ").\n\n";
1062 throw OomphLibError(error_message.str(),
1063 OOMPH_CURRENT_FUNCTION,
1064 OOMPH_EXCEPTION_LOCATION);
1065 } // if (nsegments_initial_coordinates != nsegments)
1066#endif
1067
1068 // Create a backup for the data from the original mesh
1069 // Backup for the coordinates
1070 Vector<Vector<double>> original_mesh_segment_initial_coordinate(
1071 nsegments);
1072 Vector<Vector<double>> original_mesh_segment_final_coordinate(nsegments);
1073 // Backup for the zeta values
1074 Vector<double> original_mesh_segment_initial_zeta(nsegments);
1075 Vector<double> original_mesh_segment_final_zeta(nsegments);
1076 // Backup for the arclengths
1077 Vector<double> original_mesh_segment_initial_arclength(nsegments);
1078 Vector<double> original_mesh_segment_final_arclength(nsegments);
1079 // Do the backup
1080 for (unsigned is = 0; is < nsegments; is++)
1081 {
1082 original_mesh_segment_initial_coordinate[is].resize(2);
1083 original_mesh_segment_final_coordinate[is].resize(2);
1084 for (unsigned k = 0; k < 2; k++)
1085 {
1086 original_mesh_segment_initial_coordinate[is][k] =
1087 original_mesh_pt->boundary_segment_initial_coordinate(b)[is][k];
1088 original_mesh_segment_final_coordinate[is][k] =
1089 original_mesh_pt->boundary_segment_final_coordinate(b)[is][k];
1090 }
1091 // Check if the boudary has an associated GeomObject
1092 if (this->boundary_geom_object_pt(b) != 0)
1093 {
1094 original_mesh_segment_initial_zeta[is] =
1095 original_mesh_pt->boundary_segment_initial_zeta(b)[is];
1096 original_mesh_segment_final_zeta[is] =
1097 original_mesh_pt->boundary_segment_final_zeta(b)[is];
1098 }
1099 else
1100 {
1101 original_mesh_segment_initial_arclength[is] =
1102 original_mesh_pt->boundary_segment_initial_arclength(b)[is];
1103 original_mesh_segment_final_arclength[is] =
1104 original_mesh_pt->boundary_segment_final_arclength(b)[is];
1105 }
1106 } // for (is < nsegments)
1107
1108 // Clear all the storage
1109 Boundary_segment_inverted[b].clear();
1110 Boundary_segment_initial_coordinate[b].clear();
1111 Boundary_segment_final_coordinate[b].clear();
1112
1113 Boundary_segment_initial_zeta[b].clear();
1114 Boundary_segment_final_zeta[b].clear();
1115
1116 Boundary_segment_initial_arclength[b].clear();
1117 Boundary_segment_final_arclength[b].clear();
1118
1119 // Identify each segment in the processor with the ones created
1120 // by the original mesh
1121 // -----------------------------------------------------------------
1122 // Keep track of the already identified segments
1123 std::map<unsigned, bool> segment_done;
1124 for (unsigned is = 0; is < nsegments; is++)
1125 {
1126#ifdef PARANOID
1127 // Flag to know if the segment was identified
1128 bool found_original_segment = false;
1129#endif
1130
1131 // Get the initial and final coordinates of the current segment
1132 Vector<double> current_seg_initial_coord(2);
1133 Vector<double> current_seg_final_coord(2);
1134
1135 // Get access to the initial element on the segment
1136 FiniteElement* current_seg_initial_ele_pt =
1137 segment_sorted_ele_pt[is].front();
1138
1139 // Number of nodes
1140 const unsigned nnod = current_seg_initial_ele_pt->nnode();
1141
1142 // Get the first node of the current segment
1143 Node* current_seg_first_node_pt =
1144 current_seg_initial_ele_pt->node_pt(0);
1145 if (is_inverted[current_seg_initial_ele_pt])
1146 {
1147 current_seg_first_node_pt =
1148 current_seg_initial_ele_pt->node_pt(nnod - 1);
1149 }
1150
1151 // Get access to the last element on the segment
1152 FiniteElement* current_seg_last_ele_pt =
1153 segment_sorted_ele_pt[is].back();
1154
1155 // Get the last node of the current segment
1156 Node* current_seg_last_node_pt =
1157 current_seg_last_ele_pt->node_pt(nnod - 1);
1158 if (is_inverted[current_seg_last_ele_pt])
1159 {
1160 current_seg_last_node_pt = current_seg_last_ele_pt->node_pt(0);
1161 }
1162
1163 // Get the coordinates for the first and last seg node
1164 for (unsigned i = 0; i < 2; i++)
1165 {
1166 current_seg_initial_coord[i] = current_seg_first_node_pt->x(i);
1167 current_seg_final_coord[i] = current_seg_last_node_pt->x(i);
1168 }
1169
1170 // We have got the initial and final coordinates of the current
1171 // segment, compare those with the initial and final coordinates
1172 // of the original mesh segments to identify which segments is
1173 // which
1174 for (unsigned orig_s = 0; orig_s < nsegments; orig_s++)
1175 {
1176 if (!segment_done[orig_s])
1177 {
1178 // Get the coordinates to compare
1179 Vector<double> initial_coordinate =
1180 original_mesh_segment_initial_coordinate[orig_s];
1181 Vector<double> final_coordinate =
1182 original_mesh_segment_final_coordinate[orig_s];
1183
1184 // Compute the distance initial(current)-initial(original)
1185 // coordinates
1186 double dist =
1187 ((current_seg_initial_coord[0] - initial_coordinate[0]) *
1188 (current_seg_initial_coord[0] - initial_coordinate[0])) +
1189 ((current_seg_initial_coord[1] - initial_coordinate[1]) *
1190 (current_seg_initial_coord[1] - initial_coordinate[1]));
1191 dist = sqrt(dist);
1192
1193 // If the initial node is the same, check for the last node
1195 {
1196 // Compute the distance final(current)-final(original)
1197 // coordinates
1198 dist = ((current_seg_final_coord[0] - final_coordinate[0]) *
1199 (current_seg_final_coord[0] - final_coordinate[0])) +
1200 ((current_seg_final_coord[1] - final_coordinate[1]) *
1201 (current_seg_final_coord[1] - final_coordinate[1]));
1202 dist = sqrt(dist);
1203
1204 // The final node is the same, we have identified the
1205 // segments
1207 {
1208 // Store the index that relates the previous index with the
1209 // current one
1210 current_segment_to_original_segment_index[is] = orig_s;
1211
1212 // In this case the segment is not inverted
1213 Boundary_segment_inverted[b].push_back(0);
1214
1215 // Copy the initial and final coordinates for each segment
1216 Boundary_segment_initial_coordinate[b].push_back(
1217 initial_coordinate);
1218 Boundary_segment_final_coordinate[b].push_back(
1219 final_coordinate);
1220
1221 // Check if the boundary has an associated GeomObject
1222 if (this->boundary_geom_object_pt(b) != 0)
1223 {
1224 // Copy the initial zeta value for the segment
1225 Boundary_segment_initial_zeta[b].push_back(
1226 original_mesh_segment_initial_zeta[orig_s]);
1227 Boundary_segment_final_zeta[b].push_back(
1228 original_mesh_segment_final_zeta[orig_s]);
1229 }
1230 else
1231 {
1232 // Copy the initial and final arclength for each
1233 // segment
1234 Boundary_segment_initial_arclength[b].push_back(
1235 original_mesh_segment_initial_arclength[orig_s]);
1236 Boundary_segment_final_arclength[b].push_back(
1237 original_mesh_segment_final_arclength[orig_s]);
1238 }
1239 // Mark the segment as done
1240 segment_done[orig_s] = true;
1241#ifdef PARANOID
1242 found_original_segment = true;
1243#endif
1244 break;
1245 } // The final(current) node matched with the
1246 // final(original) node
1247 } // The initial(current) node matched with the
1248 // initial(original) node
1249 else
1250 {
1251 // Check the inverted case Compute the distance
1252 // initial(current)-final(original) coordinates
1253 double dist_inv =
1254 ((current_seg_initial_coord[0] - final_coordinate[0]) *
1255 (current_seg_initial_coord[0] - final_coordinate[0])) +
1256 ((current_seg_initial_coord[1] - final_coordinate[1]) *
1257 (current_seg_initial_coord[1] - final_coordinate[1]));
1258 dist_inv = sqrt(dist_inv);
1259
1260 // If the initial node is the same as the final node of
1261 // the segment, check for the last node
1262 if (dist_inv <
1264 {
1265 // Compute the distance final(current)-initial(original)
1266 // coordinates
1267 dist_inv =
1268 ((current_seg_final_coord[0] - initial_coordinate[0]) *
1269 (current_seg_final_coord[0] - initial_coordinate[0])) +
1270 ((current_seg_final_coord[1] - initial_coordinate[1]) *
1271 (current_seg_final_coord[1] - initial_coordinate[1]));
1272 dist_inv = sqrt(dist_inv);
1273
1274 // The final node is the same as the initial node, we
1275 // have identified the segments
1276 if (dist_inv <
1278 {
1279 // Store the index that related the previous index with the
1280 // current one
1281 current_segment_to_original_segment_index[is] = orig_s;
1282
1283 // In this case the segment is inverted
1284 Boundary_segment_inverted[b].push_back(1);
1285
1286 // Copy the initial and final coordinates for each segment
1287 Boundary_segment_initial_coordinate[b].push_back(
1288 initial_coordinate);
1289 Boundary_segment_final_coordinate[b].push_back(
1290 final_coordinate);
1291
1292 // Check that the boudary has an associated GeomObject
1293 if (this->boundary_geom_object_pt(b) != 0)
1294 {
1295 // Copy the initial zeta value for the segments
1296 Boundary_segment_initial_zeta[b].push_back(
1297 original_mesh_segment_initial_zeta[orig_s]);
1298 Boundary_segment_final_zeta[b].push_back(
1299 original_mesh_segment_final_zeta[orig_s]);
1300 }
1301 else
1302 {
1303 // Copy the initial and final arclength for each segment
1304 Boundary_segment_initial_arclength[b].push_back(
1305 original_mesh_segment_initial_arclength[orig_s]);
1306 Boundary_segment_final_arclength[b].push_back(
1307 original_mesh_segment_final_arclength[orig_s]);
1308 }
1309 // Mark the segment as done
1310 segment_done[orig_s] = true;
1311#ifdef PARANOID
1312 found_original_segment = true;
1313#endif
1314 break;
1315 } // The final(current) node matched with the
1316 // initial(original) node
1317 } // The initial(current) node matched with the
1318 // final(original) node
1319 } // else (the first(current) node did not matched with the
1320 // first(original) node. Else do the inverted case
1321
1322 } // (!segment_done[orig_s])
1323
1324 } // (orig_s < nsegments)
1325
1326#ifdef PARANOID
1327 if (!found_original_segment)
1328 {
1329 std::stringstream error_message;
1330 error_message
1331 << "The (" << is << ")-th segment on the current segment was not\n"
1332 << "found when trying to identify it with the original mesh's\n"
1333 << "segment coordinates\n";
1334 throw OomphLibError(error_message.str(),
1335 OOMPH_CURRENT_FUNCTION,
1336 OOMPH_EXCEPTION_LOCATION);
1337 } // if (!found_original_segment)
1338#endif
1339 } // for (is < nsegments)
1340
1341 } // if (nsegments > 0)
1342
1343 // -------------------------------------------------------------------
1344 // Fourth: The original mesh is different from the current mesh
1345 // (this). For boundaries with no geom object associated check if it
1346 // is required to reverse the zeta values. In order to reverse the
1347 // zeta values it is required to previously compute the arclength of
1348 // the segments and store the nodes in a container (set). NOTE that
1349 // the setup_boundary_coordinate() method is not called for
1350 // boundaries with NO GeomObject associated, so this is the LAST
1351 // CHANCE to do it
1352 // -------------------------------------------------------------------
1353 // The original mesh is the same as the current mesh (this). The
1354 // setup_boundary_method() will be called only for the boundaries
1355 // with NO GeomObject associated
1356 // -------------------------------------------------------------------
1357 if (this != original_mesh_pt)
1358 {
1359 // Get the boundary arclength
1360
1361 // Get the initial and final zeta values for the boundary
1362 // (arclength) from the original mesh
1363 Vector<double> first_node_zeta_coordinate =
1364 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1365 Vector<double> last_node_zeta_coordinate =
1366 original_mesh_pt->boundary_final_zeta_coordinate(b);
1367
1368 // The boundary arclength is the maximum of the initial and final
1369 // zeta coordinate
1370 const double boundary_arclength =
1371 std::max(first_node_zeta_coordinate[0], last_node_zeta_coordinate[0]);
1372
1373 for (unsigned is = 0; is < nsegments; is++)
1374 {
1375 // Here check if need to invert the elements and the boundary
1376 // coordinates for the segments in a boundary with no GeomObject
1377 // associated
1378 if (boundary_geom_object_pt(b) == 0)
1379 {
1380 // This case only applies for the initial and iterative mesh in
1381 // the adaptation process because the method
1382 // setup_boundary_coordinates() is called by the original mesh
1383 // for boundaries with no GeomObject associated
1384
1385 // We are goind to check if it is necessary to invert the order
1386 // of the zeta values
1387
1388 // Get the first and last node of the current segment and their
1389 // zeta values (arclength)
1390
1391 // There is no need to check for nonhalo elements since the
1392 // container has only nonhalo face elements
1393
1394 // Get access to the first element on the segment
1395 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
1396
1397 // Number of nodes
1398 const unsigned nnod = first_ele_pt->nnode();
1399
1400 // Get the first node of the current segment
1401 Node* first_node_pt = first_ele_pt->node_pt(0);
1402 if (is_inverted[first_ele_pt])
1403 {
1404 first_node_pt = first_ele_pt->node_pt(nnod - 1);
1405 }
1406
1407 // Get access to the last element on the segment
1408 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
1409
1410 // Get the last node of the current segment
1411 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
1412 if (is_inverted[last_ele_pt])
1413 {
1414 last_node_pt = last_ele_pt->node_pt(0);
1415 }
1416
1417 // Get the zeta coordinates for the first and last node
1418 Vector<double> current_segment_initial_arclen(1);
1419 Vector<double> current_segment_final_arclen(1);
1420 // Is the segment in the current mesh (this) inverted?
1421 if (!Boundary_segment_inverted[b][is]) // Not inverted
1422 {
1423 first_node_pt->get_coordinates_on_boundary(
1424 b, current_segment_initial_arclen);
1425 last_node_pt->get_coordinates_on_boundary(
1426 b, current_segment_final_arclen);
1427 }
1428 else // Inverted
1429 {
1430 first_node_pt->get_coordinates_on_boundary(
1431 b, current_segment_final_arclen);
1432 last_node_pt->get_coordinates_on_boundary(
1433 b, current_segment_initial_arclen);
1434 }
1435
1436 // Once the zeta values have been obtained check if they are set
1437 // in increasing or decreasing order
1438
1439 // Flag to state that the values in the segment are in increasing
1440 // order
1441 bool increasing_order = false;
1442
1443 // If the initial zeta value is smaller than the final zeta
1444 // value then they are in increasing order
1445 if (current_segment_initial_arclen[0] <
1446 current_segment_final_arclen[0])
1447 {
1448 increasing_order = true;
1449 }
1450 // If the initial zeta value is greater than the initial zeta
1451 // value then they are in decreasing order
1452 else if (current_segment_initial_arclen[0] >
1453 current_segment_final_arclen[0])
1454 {
1455 increasing_order = false;
1456 }
1457#ifdef PARANOID
1458 else
1459 {
1460 std::stringstream error_message;
1461 error_message
1462 << "It was not possible to identify if the zeta values on "
1463 << "boundary (" << b << ")\nand segment (" << is
1464 << ") should go in "
1465 << "increasing or decreasing order.\n--- New mesh ---\n"
1466 << "Current segment initial arclength: ("
1467 << current_segment_initial_arclen[0] << ")\n"
1468 << "First node coordinates: (" << first_node_pt->x(0) << ", "
1469 << first_node_pt->x(1) << ")\n"
1470 << "Current segment final arclength: ("
1471 << current_segment_final_arclen[0] << ")\n"
1472 << "Last node coordinates: (" << last_node_pt->x(0) << ", "
1473 << last_node_pt->x(1) << ")\n"
1474 << "Current segment arclength: (" << segment_arclength[is]
1475 << ")\n";
1476 throw OomphLibError(error_message.str(),
1477 OOMPH_CURRENT_FUNCTION,
1478 OOMPH_EXCEPTION_LOCATION);
1479 }
1480#endif
1481
1482 // Now get the original initial and final arclengths and check
1483 // if they are in increasing or decreasing order
1484 const unsigned prev_s = current_segment_to_original_segment_index[is];
1485 const double original_segment_initial_arclength =
1486 original_mesh_pt->boundary_segment_initial_arclength(b)[prev_s];
1487 const double original_segment_final_arclength =
1488 original_mesh_pt->boundary_segment_final_arclength(b)[prev_s];
1489
1490 // Flag to check if the values go in increasing or decreasing
1491 // order in the original mesh segment
1492 bool original_increasing_order = false;
1493
1494 // Now check if the arclengths on the original mesh go in
1495 // increase or decrease order, this is also used to choose the
1496 // starting value to map the values in the current segment
1497 double starting_arclength = 0.0;
1498 if (original_segment_final_arclength >
1499 original_segment_initial_arclength)
1500 {
1501 // ... in increasing order in the original mesh ...
1502 original_increasing_order = true;
1503 // Select the starting arclength
1504 starting_arclength = original_segment_initial_arclength;
1505 }
1506 else if (original_segment_final_arclength <
1507 original_segment_initial_arclength)
1508 {
1509 // ... in decreasing order in the original mesh ...
1510 original_increasing_order = false;
1511 // Select the starting arclength
1512 starting_arclength = original_segment_final_arclength;
1513 }
1514#ifdef PARANOID
1515 else
1516 {
1517 std::stringstream error_message;
1518 error_message
1519 << "It was not possible to identify if the zeta values on "
1520 << "boundary (" << b << ")\nand segment (" << is
1521 << ") should go in "
1522 << "increasing or decreasing order.\n--- Original mesh ---\n"
1523 << "Original segment initial arclength: ("
1524 << original_segment_initial_arclength << ")\n"
1525 << "Original segment final arclength: ("
1526 << original_segment_final_arclength << ")\n";
1527 throw OomphLibError(error_message.str(),
1528 OOMPH_CURRENT_FUNCTION,
1529 OOMPH_EXCEPTION_LOCATION);
1530 }
1531#endif
1532
1533 // Now scale the zeta values based considering if the zeta
1534 // values from the current mesh (this) go in the same order as
1535 // in the original mesh
1536 if (increasing_order && original_increasing_order)
1537 {
1538 // Current seg
1539 // |------|
1540 // 0 ---- 1
1541 //
1542 // Is mapped to the new values
1543 // |------|
1544 // a ---- b
1545 // a = original_segment_initial_arclength
1546 // b = original_segment_final_arclength
1547 // s = starting_arclength
1548 // The mapping is given by
1549 // new_z = s + z_old * (b - a)
1550
1551 // Get the nodes associated to the segment
1552 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1553 // Go through all the nodes in the segment an change their
1554 // zeta values
1555 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1556 it != seg_nodes_pt.end();
1557 it++)
1558 {
1559 // Storing for the zeta value
1560 Vector<double> zeta(1);
1561 // Get each node
1562 Node* nod_pt = (*it);
1563 // Get the zeta value of the current node
1564 nod_pt->get_coordinates_on_boundary(b, zeta);
1565 // ... and re-assign it
1566 const double temp =
1567 starting_arclength + (zeta[0] * segment_arclength[is]);
1568 // The zeta value
1569 zeta[0] = temp / boundary_arclength;
1570 // Correct
1571 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1572 {
1573 zeta[0] = 1.0;
1574 }
1575 else if (std::fabs(zeta[0]) < 1.0e-14)
1576 {
1577 zeta[0] = 0.0;
1578 }
1579
1580 // Set the new value
1581 nod_pt->set_coordinates_on_boundary(b, zeta);
1582 } // Go through all the nodes
1583 } // if (increasing_order && original_increasing_order)
1584 else if (!increasing_order && original_increasing_order)
1585 {
1586 // Current seg
1587 // |------|
1588 // 1 ---- 0
1589 //
1590 // Is mapped to the new values
1591 // |------|
1592 // a ---- b
1593 // a = original_segment_initial_arclength
1594 // b = original_segment_final_arclength
1595 // s = starting_arclength
1596 // The mapping is given by
1597 // new_z = s + (1.0 - z_old) * (b - a)
1598
1599 // Get the nodes associated to the segment
1600 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1601 // Go through all the nodes in the segment an change their
1602 // zeta values
1603 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1604 it != seg_nodes_pt.end();
1605 it++)
1606 {
1607 // Storing for the zeta value
1608 Vector<double> zeta(1);
1609 // Get each node
1610 Node* nod_pt = (*it);
1611 // Get the zeta value of the current node
1612 nod_pt->get_coordinates_on_boundary(b, zeta);
1613 // ... and re-assign it
1614 const double temp =
1615 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1616 // The zeta value
1617 zeta[0] = temp / boundary_arclength;
1618 // Correct
1619 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1620 {
1621 zeta[0] = 1.0;
1622 }
1623 else if (std::fabs(zeta[0]) < 1.0e-14)
1624 {
1625 zeta[0] = 0.0;
1626 }
1627 // Set the new value
1628 nod_pt->set_coordinates_on_boundary(b, zeta);
1629 } // Go through all the nodes
1630 } // else if (!increasing_order && original_increasing_order)
1631 else if (increasing_order && !original_increasing_order)
1632 {
1633 // Current seg
1634 // |------|
1635 // 0 ---- 1
1636 //
1637 // Is mapped to the new values
1638 // |------|
1639 // b ---- a
1640 // a = original_segment_initial_arclength
1641 // b = original_segment_final_arclength
1642 // s = starting_arclength
1643 // The mapping is given by
1644 // new_z = s + (1.0 - z_old) * |(b - a)|
1645
1646 // Get the nodes associated to the segment
1647 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1648 // Go through all the nodes in the segment an change their
1649 // zeta values
1650 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1651 it != seg_nodes_pt.end();
1652 it++)
1653 {
1654 // Storing for the zeta value
1655 Vector<double> zeta(1);
1656 // Get each node
1657 Node* nod_pt = (*it);
1658 // Get the zeta value of the current node
1659 nod_pt->get_coordinates_on_boundary(b, zeta);
1660 // ... and re-assign it
1661 const double temp =
1662 starting_arclength + ((1.0 - zeta[0]) * segment_arclength[is]);
1663 // The zeta value
1664 zeta[0] = temp / boundary_arclength;
1665 // Correct
1666 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1667 {
1668 zeta[0] = 1.0;
1669 }
1670 else if (std::fabs(zeta[0]) < 1.0e-14)
1671 {
1672 zeta[0] = 0.0;
1673 }
1674 // Set the new value
1675 nod_pt->set_coordinates_on_boundary(b, zeta);
1676 } // Go through all the nodes
1677 } // else if (increasing_order && !original_increasing_order)
1678 else if (!increasing_order && !original_increasing_order)
1679 {
1680 // Current seg
1681 // |------|
1682 // 0 ---- 1
1683 //
1684 // Is mapped to the new values
1685 // |------|
1686 // a ---- b
1687 // a = original_segment_initial_arclength
1688 // b = original_segment_final_arclength
1689 // s = starting_arclength
1690 // The mapping is given by
1691 // new_z = s + z_old * |(b - a)|
1692
1693 // Get the nodes associated to the segment
1694 std::set<Node*> seg_nodes_pt = segment_all_nodes_pt[is];
1695 // Go through all the nodes in the segment an change their
1696 // zeta values
1697 for (std::set<Node*>::iterator it = seg_nodes_pt.begin();
1698 it != seg_nodes_pt.end();
1699 it++)
1700 {
1701 // Storing for the zeta value
1702 Vector<double> zeta(1);
1703 // Get each node
1704 Node* nod_pt = (*it);
1705 // Get the zeta value of the current node
1706 nod_pt->get_coordinates_on_boundary(b, zeta);
1707 // ... and re-assign it
1708 const double temp =
1709 starting_arclength + (zeta[0] * segment_arclength[is]);
1710 // The zeta value
1711 zeta[0] = temp / boundary_arclength;
1712 // Correct
1713 if (std::fabs(zeta[0] - 1.0) < 1.0e-14)
1714 {
1715 zeta[0] = 1.0;
1716 }
1717 else if (std::fabs(zeta[0]) < 1.0e-14)
1718 {
1719 zeta[0] = 0.0;
1720 }
1721 // Set the new value
1722 nod_pt->set_coordinates_on_boundary(b, zeta);
1723 } // Go through all the nodes
1724 } // else if (!increasing_order && !original_increasing_order)
1725
1726#ifdef PARANOID
1727 // Verify that the z values of the first and last node are not
1728 // out of the range [0,1]
1729 for (std::list<FiniteElement*>::iterator it_list =
1730 segment_sorted_ele_pt[is].begin();
1731 it_list != segment_sorted_ele_pt[is].end();
1732 it_list++)
1733 {
1734 // Number of nodes in the segment
1735 const unsigned nnod = (*it_list)->nnode();
1736
1737 // Get the first node of the current segment
1738 Node* first_node_pt = (*it_list)->node_pt(0);
1739 if (is_inverted[(*it_list)])
1740 {
1741 first_node_pt = (*it_list)->node_pt(nnod - 1);
1742 }
1743
1744 // Get the last node of the current segment
1745 Node* last_node_pt = (*it_list)->node_pt(nnod - 1);
1746 if (is_inverted[(*it_list)])
1747 {
1748 last_node_pt = (*it_list)->node_pt(0);
1749 }
1750
1751 // The z value for the first node
1752 Vector<double> zeta(1);
1753 first_node_pt->get_coordinates_on_boundary(b, zeta);
1754 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1755 {
1756 std::ostringstream error_message;
1757 error_message
1758 << "The boundary coordinate of the first node on boundary ("
1759 << b << ")\nand segment (" << is << ") is out of the "
1760 << "allowed values [0,1]\n"
1761 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1762 << "The vertex coordinates are: (" << first_node_pt->x(0)
1763 << ", " << first_node_pt->x(1) << ")\n";
1764 throw OomphLibError(error_message.str(),
1765 OOMPH_CURRENT_FUNCTION,
1766 OOMPH_EXCEPTION_LOCATION);
1767 }
1768
1769 // The z value for the last node
1770 last_node_pt->get_coordinates_on_boundary(b, zeta);
1771 if (zeta[0] < 0.0 || zeta[0] > 1.0)
1772 {
1773 std::ostringstream error_message;
1774 error_message
1775 << "The boundary coordinate of the last node on boundary (" << b
1776 << ")\nand segment (" << is << ") is out of the "
1777 << "allowed values [0,1]\n"
1778 << "The node boundary coordinate: (" << zeta[0] << ")\n"
1779 << "The vertex coordinates are: (" << last_node_pt->x(0) << ", "
1780 << last_node_pt->x(1) << ")\n";
1781 throw OomphLibError(error_message.str(),
1782 OOMPH_CURRENT_FUNCTION,
1783 OOMPH_EXCEPTION_LOCATION);
1784 }
1785 }
1786#endif // #ifdef PARANOID
1787
1788 } // if (boundary_geom_object_pt(b)==0)
1789
1790 } // for (is < nsegments)
1791
1792 } // if (this != original_mesh_pt)
1793
1794 // ------------------------------------------------------------------
1795 // Copy the corrected (possible reversed) info. to the containers of
1796 // the current mesh
1797 // ------------------------------------------------------------------
1798 // Check if there are segments of b boundary in this processor
1799 if (nsegments > 0)
1800 {
1801 // Copy the initial and final coordinates
1802 Boundary_initial_coordinate[b] =
1803 original_mesh_pt->boundary_initial_coordinate(b);
1804
1805 Boundary_final_coordinate[b] =
1806 original_mesh_pt->boundary_final_coordinate(b);
1807
1808 // The initial and final zeta coordinates (In case of a geometric
1809 // object those are the limits of the geom object)
1810 Boundary_initial_zeta_coordinate[b] =
1811 original_mesh_pt->boundary_initial_zeta_coordinate(b);
1812
1813 Boundary_final_zeta_coordinate[b] =
1814 original_mesh_pt->boundary_final_zeta_coordinate(b);
1815
1816 } // if (nsegments > 0)
1817
1818 // Set the flag to indicate that the zeta values have been assigned
1819 // for the current boundary
1820 Assigned_segments_initial_zeta_values[b] = true;
1821
1822 // Clean all the created face elements
1823 for (unsigned i = 0; i < nele; i++)
1824 {
1825 delete face_el_pt[i];
1826 face_el_pt[i] = 0;
1827 }
1828 }
1829
1830 //======================================================================
1831 /// Compute the boundary segments connectivity for those
1832 /// boundaries that were splited during the distribution process
1833 /// and also the initial zeta values for each segment (the initial
1834 /// and final boundary nodes coordinates)
1835 //======================================================================
1836 template<class ELEMENT>
1839 const unsigned& b)
1840 {
1841 // ------------------------------------------------------------------
1842 // First: Get the face elements associated with the current boundary
1843 // ------------------------------------------------------------------
1844
1845 // Get the communicator of the mesh
1846 OomphCommunicator* comm_pt = this->communicator_pt();
1847
1848 // Get the number of processors
1849 const unsigned nproc = comm_pt->nproc();
1850 // Get the rank of the current processor
1851 const unsigned my_rank = comm_pt->my_rank();
1852
1853 // Temporary storage for face elements
1854 Vector<FiniteElement*> all_face_ele_pt;
1855
1856 // Flag to know whether we are working with an internal open curve
1857 // and then re-assign the initial and final zeta coordinates for
1858 // each segment (only used when the mesh is distributed)
1859 bool is_internal_boundary = false;
1860
1861 // map to associate the face element to the bulk element, necessary
1862 // to attach halo face elements at both sides of each found segment
1863 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
1864
1865 // Select the boundary face elements, using the criteria of highest
1866 // processor in charge and bottom-left element
1867 select_boundary_face_elements(
1868 all_face_ele_pt, b, is_internal_boundary, face_to_bulk_element_pt);
1869
1870 // Get the number of face elements
1871 const unsigned n_all_face_ele = all_face_ele_pt.size();
1872
1873 // ----------------------------------------------------------------
1874 // Second: Sort the face elements, only consider nonhalo elements
1875 // ----------------------------------------------------------------
1876
1877 // A flag vector to mark those face elements that are considered as
1878 // halo in the current processor
1879 std::vector<bool> is_halo_face_element(n_all_face_ele, false);
1880
1881 // Count the total number of non halo face elements
1882 unsigned nnon_halo_face_elements = 0;
1883
1884 // Only mark the face elements as halo if the mesh is marked as
1885 // distributed
1886 for (unsigned ie = 0; ie < n_all_face_ele; ie++)
1887 {
1888 FiniteElement* face_ele_pt = all_face_ele_pt[ie];
1889 // Get the bulk element
1890 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_ele_pt];
1891 // Check if the bulk element is halo
1892 if (!tmp_bulk_ele_pt->is_halo())
1893 {
1894 // Set the flag for non halo element
1895 is_halo_face_element[ie] = false;
1896 // Increase the non halo elements counter
1897 nnon_halo_face_elements++;
1898 }
1899 else
1900 {
1901 // Mark the face element as halo
1902 is_halo_face_element[ie] = true;
1903 }
1904
1905 } // for (ie < n_ele)
1906
1907 // Get the total number of halo face elements
1908 const unsigned nhalo_face_element =
1909 n_all_face_ele - nnon_halo_face_elements;
1910
1911 // The vector of list to store the "segments" that compound the
1912 // boundary (segments may appear only in a distributed mesh)
1913 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
1914
1915 // Number of already sorted face elements (only nonhalo elements for
1916 // a distributed mesh)
1917 unsigned nsorted_face_elements = 0;
1918
1919 // Keep track of who's done (this apply to nonhalo only, remember we
1920 // are only working with halo elements)
1921 std::map<FiniteElement*, bool> done_el;
1922
1923 // Keep track of which element is inverted (in distributed mesh the
1924 // elements may be inverted with respect to the segment they belong)
1925 std::map<FiniteElement*, bool> is_inverted;
1926
1927 // Iterate until all possible segments have been created
1928 while (nsorted_face_elements < nnon_halo_face_elements)
1929 {
1930 // The ordered list of face elements (in a distributed mesh a
1931 // collection of contiguous face elements define a segment)
1932 std::list<FiniteElement*> sorted_el_pt;
1933 sorted_el_pt.clear();
1934
1935#ifdef PARANOID
1936 // Select an initial element for the segment (the first not done
1937 // nonhalo element)
1938 bool found_initial_face_element = false;
1939#endif
1940
1941 FiniteElement* ele_face_pt = 0;
1942
1943 unsigned iface = 0;
1944 for (iface = 0; iface < n_all_face_ele; iface++)
1945 {
1946 if (!is_halo_face_element[iface])
1947 {
1948 ele_face_pt = all_face_ele_pt[iface];
1949 // If not done then take it as initial face element
1950 if (!done_el[ele_face_pt])
1951 {
1952#ifdef PARANOID
1953 found_initial_face_element = true;
1954#endif
1955 nsorted_face_elements++;
1956 iface++; // The next element number
1957 sorted_el_pt.push_back(ele_face_pt);
1958 // Mark as done
1959 done_el[ele_face_pt] = true;
1960 break;
1961 }
1962 }
1963 } // for (iface < nele)
1964
1965#ifdef PARANOID
1966 if (!found_initial_face_element)
1967 {
1968 std::ostringstream error_message;
1969 error_message
1970 << "Could not find an initial face element for the current segment\n";
1971 // << "----- Possible memory leak -----\n";
1972 throw OomphLibError(error_message.str(),
1973 OOMPH_CURRENT_FUNCTION,
1974 OOMPH_EXCEPTION_LOCATION);
1975 }
1976#endif
1977
1978 // Number of nodes
1979 const unsigned nnod = ele_face_pt->nnode();
1980
1981 // Left and rightmost nodes (the left and right nodes of the
1982 // current face element)
1983 Node* left_node_pt = ele_face_pt->node_pt(0);
1984 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
1985
1986 // Continue iterating if a new face element has been added to the
1987 // list
1988 bool face_element_added = false;
1989
1990 // While a new face element has been added to the set of sorted
1991 // face elements then re-iterate
1992 do
1993 {
1994 // Start from the next face element since we have already added
1995 // the previous one as the initial face element (any previous
1996 // face element had to be added on previous iterations)
1997 for (unsigned iiface = iface; iiface < n_all_face_ele; iiface++)
1998 {
1999 // Re-start flag
2000 face_element_added = false;
2001
2002 // Get the candidate element
2003 ele_face_pt = all_face_ele_pt[iiface];
2004
2005 // Check that the candidate element has not been done and is
2006 // not a halo element
2007 if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
2008 {
2009 // Get the left and right nodes of the current element
2010 Node* local_left_node_pt = ele_face_pt->node_pt(0);
2011 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
2012
2013 // New element fits at the left of segment and is not inverted
2014 if (left_node_pt == local_right_node_pt)
2015 {
2016 left_node_pt = local_left_node_pt;
2017 sorted_el_pt.push_front(ele_face_pt);
2018 is_inverted[ele_face_pt] = false;
2019 face_element_added = true;
2020 }
2021 // New element fits at the left of segment and is inverted
2022 else if (left_node_pt == local_left_node_pt)
2023 {
2024 left_node_pt = local_right_node_pt;
2025 sorted_el_pt.push_front(ele_face_pt);
2026 is_inverted[ele_face_pt] = true;
2027 face_element_added = true;
2028 }
2029 // New element fits on the right of segment and is not inverted
2030 else if (right_node_pt == local_left_node_pt)
2031 {
2032 right_node_pt = local_right_node_pt;
2033 sorted_el_pt.push_back(ele_face_pt);
2034 is_inverted[ele_face_pt] = false;
2035 face_element_added = true;
2036 }
2037 // New element fits on the right of segment and is inverted
2038 else if (right_node_pt == local_right_node_pt)
2039 {
2040 right_node_pt = local_left_node_pt;
2041 sorted_el_pt.push_back(ele_face_pt);
2042 is_inverted[ele_face_pt] = true;
2043 face_element_added = true;
2044 }
2045
2046 if (face_element_added)
2047 {
2048 done_el[ele_face_pt] = true;
2049 nsorted_face_elements++;
2050 break;
2051 }
2052
2053 } // if (!(done_el[ele_face_pt] || is_halo_face_element[iiface]))
2054 } // for (iiface<nnon_halo_face_element)
2055 } while (face_element_added &&
2056 (nsorted_face_elements < nnon_halo_face_elements));
2057
2058 // Store the created segment in the vector of segments
2059 segment_sorted_ele_pt.push_back(sorted_el_pt);
2060
2061 } // while(nsorted_face_elements < nnon_halo_face_elements);
2062
2063 // -----------------------------------------------------------------
2064 // Third: We have the face elements sorted (in segments), now assign
2065 // boundary coordinates to the nodes in the segments, this is the
2066 // LOCAL boundary coordinate and further communication is needed to
2067 // compute the GLOBAL boundary coordinates
2068 // -----------------------------------------------------------------
2069
2070 // Vector of sets that stores the nodes of each segment based on a
2071 // lexicographically order starting from the bottom left node of
2072 // each segment
2073 Vector<std::set<Node*>> segment_all_nodes_pt;
2074
2075 // The number of segments in this processor
2076 const unsigned nsegments = segment_sorted_ele_pt.size();
2077 // DEBP(nsegments);
2078
2079#ifdef PARANOID
2080 if (nnon_halo_face_elements > 0 && nsegments == 0)
2081 {
2082 std::ostringstream error_message;
2083 error_message
2084 << "The number of segments is zero, but the number of nonhalo\n"
2085 << "elements is: (" << nnon_halo_face_elements << ")\n";
2086 throw OomphLibError(
2087 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
2088 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
2089#endif
2090
2091 // The arclength of each segment in the current processor
2092 Vector<double> segment_arclength(nsegments);
2093
2094 // The number of vertices of each segment
2095 Vector<unsigned> nvertices_per_segment(nsegments);
2096
2097 // The initial zeta for the segment
2098 Vector<double> initial_zeta_segment(nsegments);
2099
2100 // The final zeta for the segment
2101 Vector<double> final_zeta_segment(nsegments);
2102
2103 // Go through all the segments and compute its ARCLENGTH (if the
2104 // boundary has a GeomObject associated then assign the initial and
2105 // final zeta values for the segment)
2106 for (unsigned is = 0; is < nsegments; is++)
2107 {
2108#ifdef PARANOID
2109 if (segment_sorted_ele_pt[is].size() == 0)
2110 {
2111 std::ostringstream error_message;
2112 error_message << "The (" << is << ")-th segment has no elements\n";
2113 throw OomphLibError(error_message.str(),
2114 OOMPH_CURRENT_FUNCTION,
2115 OOMPH_EXCEPTION_LOCATION);
2116 } // if (segment_sorted_ele_pt[is].size() == 0)
2117#endif
2118
2119 // Get access to the first element on the segment
2120 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
2121
2122 // Number of nodes
2123 const unsigned nnod = first_ele_pt->nnode();
2124
2125 // Get the first node of the current segment
2126 Node* first_node_pt = first_ele_pt->node_pt(0);
2127 if (is_inverted[first_ele_pt])
2128 {
2129 first_node_pt = first_ele_pt->node_pt(nnod - 1);
2130 }
2131
2132 // Coordinates of left node
2133 double x_left = first_node_pt->x(0);
2134 double y_left = first_node_pt->x(1);
2135
2136 // Initialise boundary coordinate (local boundary coordinate for
2137 // boundaries with more than one segment)
2138 Vector<double> zeta(1, 0.0);
2139
2140 // If we have associated a GeomObject then it is not necessary to
2141 // compute the arclength, only read the values from the nodes at
2142 // the edges and set the initial and final zeta segment values
2143 if (this->boundary_geom_object_pt(b) != 0)
2144 {
2145 // Get the initial node coordinate
2146 first_node_pt->get_coordinates_on_boundary(b, zeta);
2147 // Set the initial zeta segment value
2148 initial_zeta_segment[is] = zeta[0];
2149
2150 // Get access to the last element on the segment
2151 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
2152
2153 // Get the last node of the current segment
2154 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2155 if (is_inverted[last_ele_pt])
2156 {
2157 last_node_pt = last_ele_pt->node_pt(0);
2158 }
2159
2160 // Get the final node coordinate
2161 last_node_pt->get_coordinates_on_boundary(b, zeta);
2162 // Set the final zeta segment value
2163 final_zeta_segment[is] = zeta[0];
2164 }
2165
2166 // Sort the nodes in the segment (lexicographically bottom left
2167 // node)
2168 std::set<Node*> local_nodes_pt;
2169 // Insert the first node
2170 local_nodes_pt.insert(first_node_pt);
2171
2172 // Now loop over nodes in order and increase the ARCLENGTH
2173 for (std::list<FiniteElement*>::iterator it =
2174 segment_sorted_ele_pt[is].begin();
2175 it != segment_sorted_ele_pt[is].end();
2176 it++)
2177 {
2178 // Get the pointer to the element
2179 FiniteElement* el_pt = (*it);
2180
2181 // Start node and increment
2182 unsigned k_nod = 1;
2183 int nod_diff = 1;
2184 // Access nodes in reverse?
2185 if (is_inverted[el_pt])
2186 {
2187 k_nod = nnod - 2;
2188 nod_diff = -1;
2189 }
2190
2191 // Loop over nodes in the face element
2192 for (unsigned j = 1; j < nnod; j++)
2193 {
2194 Node* nod_pt = el_pt->node_pt(k_nod);
2195 k_nod += nod_diff;
2196
2197 // Coordinates of right node
2198 double x_right = nod_pt->x(0);
2199 double y_right = nod_pt->x(1);
2200
2201 // Increment boundary coordinate (the arclength)
2202 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
2203 (y_right - y_left) * (y_right - y_left));
2204
2205 // When we have a GeomObject associated to the boundary we already
2206 // know the zeta values for the nodes, there is no need to compute
2207 // the arclength
2208 // if (this->boundary_geom_object_pt(b)==0)
2209 // {
2210 // // Set boundary coordinate
2211 // // nod_pt->set_coordinates_on_boundary(b, zeta);
2212 // }
2213
2214 // Increment reference coordinate
2215 x_left = x_right;
2216 y_left = y_right;
2217
2218 // Get lexicographically bottom left node but only
2219 // use vertex nodes as candidates
2220 local_nodes_pt.insert(nod_pt);
2221
2222 } // for (j < nnod)
2223
2224 } // iterator over the elements in the segment
2225
2226 // Info. to be passed to other processors
2227 // The initial arclength for the segment that goes after this depends
2228 // on the current segment arclength
2229 segment_arclength[is] = zeta[0];
2230
2231 // Info. to be passed to the other processors
2232 // The initial vertex number for the segment that goes after this
2233 // depends on the current segment vertices number
2234 nvertices_per_segment[is] = local_nodes_pt.size();
2235
2236 // Add the nodes for the corresponding segment in the container
2237 segment_all_nodes_pt.push_back(local_nodes_pt);
2238
2239 // The attaching of the halo elements at both sides of the segments is
2240 // performed only if segments connectivity needs to be computed
2241
2242 } // for (is < nsegments)
2243
2244 // Container to store the number of vertices before each segment,
2245 // initialise to zero in case we have a non distributed boundary
2246 Vector<unsigned> nvertices_before_segment(nsegments, 0);
2247
2248 // Store the initial arclength for each segment of boundary in the
2249 // current processor, initalise to zero in case we have a non
2250 // distributed boundary
2251 Vector<double> initial_segment_arclength(nsegments, 0.0);
2252
2253 // Info. to be passed to other processors
2254 // If the boundary is distributed we need to know which processors does
2255 // have the initial and final segments, this helps to get the first and
2256 // last nodes coordinates (info. used to scale the bound coordinates)
2257
2258 // Processors with the initial and final segment
2259 unsigned proc_with_initial_seg = 0;
2260 unsigned proc_with_final_seg = 0;
2261
2262 // ... and the index of those segments (only of interest in the
2263 // processors that have the initial and final segments)
2264 unsigned initial_segment = 0;
2265 unsigned final_segment = 0;
2266
2267 // Each segment needs to know whether it has to be inverted or not
2268 // Store whether a segment needs to be inverted or not
2269 Vector<unsigned> segment_inverted(nsegments);
2270
2271 // Before attaching the halo elements create a copy of the data
2272 // structure without halo elements
2273 Vector<std::list<FiniteElement*>> segment_sorted_nonhalo_ele_pt(nsegments);
2274 for (unsigned is = 0; is < nsegments; is++)
2275 {
2276 for (std::list<FiniteElement*>::iterator it_seg =
2277 segment_sorted_ele_pt[is].begin();
2278 it_seg != segment_sorted_ele_pt[is].end();
2279 it_seg++)
2280 {
2281 segment_sorted_nonhalo_ele_pt[is].push_back((*it_seg));
2282 }
2283
2284 } // for (is < nsegments)
2285
2286 // --------------------------------------------------------------
2287 // Attach the halo elements at both sides of the segments
2288 for (unsigned is = 0; is < nsegments; is++)
2289 {
2290 // Get access to the first element on the segment
2291 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
2292
2293 // Number of nodes
2294 const unsigned nnod = first_ele_pt->nnode();
2295
2296 // Get the first node of the current segment
2297 Node* first_node_pt = first_ele_pt->node_pt(0);
2298 if (is_inverted[first_ele_pt])
2299 {
2300 first_node_pt = first_ele_pt->node_pt(nnod - 1);
2301 }
2302
2303 // Get access to the last element on the segment
2304 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
2305
2306 // Get the last node of the current segment
2307 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
2308 if (is_inverted[last_ele_pt])
2309 {
2310 last_node_pt = last_ele_pt->node_pt(0);
2311 }
2312
2313 // -----------------------------------------------------------------
2314 // Fourth: Now attach the halo elements to the left and right side
2315 // of each segment
2316 // -----------------------------------------------------------------
2317 bool attached_left_halo = false;
2318 bool attached_right_halo = false;
2319 if (nhalo_face_element > 0)
2320 {
2321 for (unsigned iiface = 0; iiface < n_all_face_ele; iiface++)
2322 {
2323 // Get the candidate element
2324 FiniteElement* halo_face_ele_pt = all_face_ele_pt[iiface];
2325
2326 // Check that the element is a halo face element, we do not check
2327 // if the element has been already done since the halo elements
2328 // may be connected to more than one segment (2 at most), to the
2329 // left and right of different segments
2330 //
2331 // Segment k Halo Segment r
2332 // |---|---|---| |xxx| |---|---|---|
2333 //
2334 // Segment k Halo Segment r
2335 // |---|---|---|xxx|---|---|---|
2336 //
2337 if (is_halo_face_element[iiface])
2338 {
2339 // Get its left and right nodes
2340 Node* left_node_pt = halo_face_ele_pt->node_pt(0);
2341 Node* right_node_pt = halo_face_ele_pt->node_pt(nnod - 1);
2342 // The halo element fits to the left of segment
2343 if (!attached_left_halo && (first_node_pt == right_node_pt ||
2344 first_node_pt == left_node_pt))
2345 {
2346 // Add the halo element to the left of the segment
2347 segment_sorted_ele_pt[is].push_front(halo_face_ele_pt);
2348
2349 // Once a halo face element has been added to the left
2350 // mark as found halo to the left
2351 attached_left_halo = true;
2352 }
2353 // The halo element fits to the right of the segment
2354 else if (!attached_right_halo && (last_node_pt == left_node_pt ||
2355 last_node_pt == right_node_pt))
2356 {
2357 // Add the halo element to the right of the segment
2358 segment_sorted_ele_pt[is].push_back(halo_face_ele_pt);
2359 // Once a halo face element has been added to the right
2360 // mark as found halo to the right
2361 attached_right_halo = true;
2362 }
2363 // If we have already found elements to left and right then
2364 // break the loop
2365 if (attached_left_halo && attached_right_halo)
2366 {
2367 break;
2368 }
2369
2370 } // if (is_halo_face_element[iiface])
2371
2372 } // for (iiface < nel)
2373
2374 } // if (nhalo_face_element > 0)
2375
2376 } // for (is < nsegments)
2377
2378 // The segments now have local coordinates assigned and halo
2379 // elements attached to them. Store that info. in the corresponding
2380 // data structures and be ready to send that info. to a root
2381 // processor. The root processor will be in charge of computing the
2382 // boundary coordinates for each segment of the boundary.
2383
2384 // For each segment store the following information
2385 // --------------------------------------------------------------------
2386 // Stores the "rank" of the processor to the left of each segment,
2387 // zero if there is no processor to the left which states that the
2388 // segment is the first one on the boundary
2389 Vector<unsigned> left_processor_plus_one(nsegments);
2390
2391 // Stores the "rank" of the processor to the right of each segment,
2392 // zero if there is no processor to the right which states that the
2393 // segment is the last one on the boundary
2394 Vector<unsigned> right_processor_plus_one(nsegments);
2395
2396 // The id. of the halo element to the left of the segment, note that
2397 // this info. is not necessary if there is no processor to the left
2398 // of the segment
2399 Vector<unsigned> left_halo_element(nsegments);
2400
2401 // The id. of the halo element to the right of the segment, note that
2402 // this info. is not necessary if there is no processor to the right
2403 // of the segment
2404 Vector<unsigned> right_halo_element(nsegments);
2405
2406 // The id. of the haloed element to the left of the segment, note that
2407 // this info. is not necessary if there is no processor to the left
2408 // of the segment
2409 Vector<unsigned> left_haloed_element(nsegments);
2410
2411 // The id. of the haloed element to the right of the segment, note
2412 // that this info. is not necessary if there is no processor to the
2413 // right of the segment
2414 Vector<unsigned> right_haloed_element(nsegments);
2415
2416 // Go through all the segments and get the info.
2417 for (unsigned is = 0; is < nsegments; is++)
2418 {
2419 // Get access to the left most face element on the segment
2420 FiniteElement* left_face_ele_pt = segment_sorted_ele_pt[is].front();
2421
2422 // Get the corresponding bulk element and check whether it is a halo
2423 // element or not
2424 FiniteElement* tmp_left_bulk_ele_pt =
2425 face_to_bulk_element_pt[left_face_ele_pt];
2426
2427 // Check if the bulk element is halo
2428 if (tmp_left_bulk_ele_pt->is_halo())
2429 {
2430 // Then store the corresponding info.
2431 int left_proc = tmp_left_bulk_ele_pt->non_halo_proc_ID();
2432#ifdef PARANOID
2433 if (left_proc < 0)
2434 {
2435 std::ostringstream error_message;
2436 error_message
2437 << "The current bulk element (left) is marked as halo but "
2438 << "the processor holding\nthe non-halo counterpart is "
2439 << "negative!\n";
2440 throw OomphLibError(error_message.str(),
2441 OOMPH_CURRENT_FUNCTION,
2442 OOMPH_EXCEPTION_LOCATION);
2443 }
2444#endif
2445 // The processor "rank" to the left
2446 unsigned left_processor = static_cast<unsigned>(left_proc);
2447 left_processor_plus_one[is] = left_processor + 1;
2448
2449 // Now get the id of the halo element to the left
2450 GeneralisedElement* left_element_pt = tmp_left_bulk_ele_pt;
2451
2452 // Get the halo elements with left processor
2453 Vector<GeneralisedElement*> left_halo_element_pt =
2454 this->halo_element_pt(left_processor);
2455
2456#ifdef PARANOID
2457 // Flag to state that the halo element was found
2458 bool left_halo_element_found = false;
2459#endif
2460
2461 const unsigned n_halo_left = left_halo_element_pt.size();
2462 for (unsigned lh = 0; lh < n_halo_left; lh++)
2463 {
2464 if (left_element_pt == left_halo_element_pt[lh])
2465 {
2466 left_halo_element[is] = lh;
2467#ifdef PARANOID
2468 left_halo_element_found = true;
2469#endif
2470 break;
2471 }
2472 } // for (lh < n_halo_left)
2473
2474#ifdef PARANOID
2475 if (!left_halo_element_found)
2476 {
2477 std::ostringstream error_message;
2478 error_message
2479 << "The current bulk element (left) marked as halo was "
2480 << "not found in the vector of halo\nelements associated "
2481 << "with the (" << left_processor << ") processor.\n\n";
2482 throw OomphLibError(error_message.str(),
2483 OOMPH_CURRENT_FUNCTION,
2484 OOMPH_EXCEPTION_LOCATION);
2485 } // if (!left_halo_element_found)
2486#endif
2487
2488 // Get the left-most nonhalo element (use the backup list of
2489 // nonhalo elements)
2490 left_face_ele_pt = segment_sorted_nonhalo_ele_pt[is].front();
2491
2492 // Get the corresponding bulk element
2493 tmp_left_bulk_ele_pt = face_to_bulk_element_pt[left_face_ele_pt];
2494
2495#ifdef PARANOID
2496 // This element should not be marked as halo
2497 if (tmp_left_bulk_ele_pt->is_halo())
2498 {
2499 std::ostringstream error_message;
2500 error_message
2501 << "The bulk element represetation of the left-most nonhalo face\n"
2502 << "element of the current segment (" << is
2503 << ") is marked as halo,\n"
2504 << "but the face element created from it is nonhalo\n";
2505 throw OomphLibError(error_message.str(),
2506 OOMPH_CURRENT_FUNCTION,
2507 OOMPH_EXCEPTION_LOCATION);
2508 } // if (tmp_left_bulk_ele_pt->is_halo())
2509#endif
2510
2511 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2512 // to search in the haloed vector
2513 left_element_pt = tmp_left_bulk_ele_pt;
2514
2515#ifdef PARANOID
2516 // Flag to state that the haloed element was found
2517 bool left_haloed_element_found = false;
2518#endif
2519
2520 // Now get the id for the haloed element to the left, get the
2521 // haloed elements from the processor to the left
2522 Vector<GeneralisedElement*> left_haloed_element_pt =
2523 this->haloed_element_pt(left_processor);
2524
2525 const unsigned nhaloed_left = left_haloed_element_pt.size();
2526 for (unsigned lhd = 0; lhd < nhaloed_left; lhd++)
2527 {
2528 if (left_element_pt == left_haloed_element_pt[lhd])
2529 {
2530 left_haloed_element[is] = lhd;
2531#ifdef PARANOID
2532 left_haloed_element_found = true;
2533#endif
2534 break;
2535 }
2536 } // for (lhd < nhaloed_left)
2537
2538#ifdef PARANOID
2539 if (!left_haloed_element_found)
2540 {
2541 std::ostringstream error_message;
2542 error_message
2543 << "The current bulk element (left) marked as haloed was "
2544 << "not found in the vector of haloed\nelements associated "
2545 << "with processor (" << left_processor << ").\n";
2546 throw OomphLibError(error_message.str(),
2547 OOMPH_CURRENT_FUNCTION,
2548 OOMPH_EXCEPTION_LOCATION);
2549 }
2550#endif
2551 } // if (tmp_left_bulk_ele_pt->is_halo())
2552 else
2553 {
2554 // If not halo then state the info. to indicate that
2555 left_processor_plus_one[is] = 0;
2556 // Null this info.
2557 left_halo_element[is] = 0;
2558 // Null this info.
2559 left_haloed_element[is] = 0;
2560 }
2561
2562 // Get access to the right most face element on the segment
2563 FiniteElement* right_face_ele_pt = segment_sorted_ele_pt[is].back();
2564
2565 // Get the corresponding bulk element and check whether it is
2566 // a halo element or not
2567 FiniteElement* tmp_right_bulk_ele_pt =
2568 face_to_bulk_element_pt[right_face_ele_pt];
2569
2570 // Check if the bulk element is halo
2571 if (tmp_right_bulk_ele_pt->is_halo())
2572 {
2573 // Then store the corresponding info.
2574 int right_proc = tmp_right_bulk_ele_pt->non_halo_proc_ID();
2575#ifdef PARANOID
2576 if (right_proc < 0)
2577 {
2578 std::ostringstream error_message;
2579 error_message
2580 << "The current bulk element (right) is marked as halo but "
2581 << "the processor holding\nthe non-halo counterpart is "
2582 << "negative!\n";
2583 throw OomphLibError(error_message.str(),
2584 "TriangleMesh::compute_boundary_segments_"
2585 "connectivity_and_initial_zeta_values()",
2586 OOMPH_EXCEPTION_LOCATION);
2587 }
2588#endif
2589 // The processor "rank" to the right
2590 unsigned right_processor = static_cast<unsigned>(right_proc);
2591 right_processor_plus_one[is] = right_processor + 1;
2592
2593 // Now get the id of the halo element to the right
2594 GeneralisedElement* right_element_pt = tmp_right_bulk_ele_pt;
2595
2596 // Get the halo elements with right processor
2597 Vector<GeneralisedElement*> right_halo_element_pt =
2598 this->halo_element_pt(right_processor);
2599
2600#ifdef PARANOID
2601 // Flag to state that the halo element was found
2602 bool right_halo_element_found = false;
2603#endif
2604
2605 const unsigned nhalo_right = right_halo_element_pt.size();
2606 for (unsigned rh = 0; rh < nhalo_right; rh++)
2607 {
2608 if (right_element_pt == right_halo_element_pt[rh])
2609 {
2610 right_halo_element[is] = rh;
2611#ifdef PARANOID
2612 right_halo_element_found = true;
2613#endif
2614 break;
2615 }
2616 } // for (rh < nhalo_right)
2617#ifdef PARANOID
2618 if (!right_halo_element_found)
2619 {
2620 std::ostringstream error_message;
2621 error_message
2622 << "The current bulk element (right) marked as halo was not "
2623 << "found in the vector of halo\nelements associated with "
2624 << "the (" << right_processor << ") processor.\n\n";
2625 throw OomphLibError(error_message.str(),
2626 "TriangleMesh::compute_boundary_segments_"
2627 "connectivity_and_initial_zeta_values()",
2628 OOMPH_EXCEPTION_LOCATION);
2629 }
2630#endif
2631
2632 // Get the right-most nonhalo element (use the backup list of
2633 // nonhalo elements)
2634 right_face_ele_pt = segment_sorted_nonhalo_ele_pt[is].back();
2635
2636 // Get the corresponding bulk element
2637 tmp_right_bulk_ele_pt = face_to_bulk_element_pt[right_face_ele_pt];
2638#ifdef PARANOID
2639 // This element should not be marked as halo
2640 if (tmp_right_bulk_ele_pt->is_halo())
2641 {
2642 std::ostringstream error_message;
2643 error_message
2644 << "The bulk element represetation of the right-most nonhalo face\n"
2645 << "element of the current segment (" << is
2646 << ") is marked as halo,\n"
2647 << "but the face element created from it is nonhalo\n";
2648 throw OomphLibError(error_message.str(),
2649 "TriangleMesh::compute_boundary_segments_"
2650 "connectivity_and_initial_zeta_values()",
2651 OOMPH_EXCEPTION_LOCATION);
2652 } // if (tmp_right_bulk_ele_pt->is_halo())
2653#endif
2654
2655 // Cast from "FiniteElement*" to "GeneralisedElement*" to be able
2656 // to search in the haloed vector
2657 right_element_pt = tmp_right_bulk_ele_pt;
2658
2659#ifdef PARANOID
2660 // Flag to state that the haloed element was found
2661 bool right_haloed_element_found = false;
2662#endif
2663
2664 // Now get the id for the haloed element to the right
2665 Vector<GeneralisedElement*> right_haloed_element_pt =
2666 this->haloed_element_pt(right_processor);
2667
2668 const unsigned nhaloed_right = right_haloed_element_pt.size();
2669 for (unsigned rhd = 0; rhd < nhaloed_right; rhd++)
2670 {
2671 if (right_element_pt == right_haloed_element_pt[rhd])
2672 {
2673 right_haloed_element[is] = rhd;
2674#ifdef PARANOID
2675 right_haloed_element_found = true;
2676#endif
2677 break;
2678 }
2679 } // for (rhd < nhaloed_right)
2680
2681#ifdef PARANOID
2682 if (!right_haloed_element_found)
2683 {
2684 std::ostringstream error_message;
2685 error_message
2686 << "The current bulk element (right) marked as haloed was not "
2687 << "found in the vector of haloed\nelements associated with "
2688 << "the (" << right_processor << ") processor.\n\n";
2689 throw OomphLibError(error_message.str(),
2690 "TriangleMesh::compute_boundary_segments_"
2691 "connectivity_and_initial_zeta_values()",
2692 OOMPH_EXCEPTION_LOCATION);
2693 }
2694#endif
2695
2696 } // if (tmp_right_bulk_ele_pt->is_halo())
2697 else
2698 {
2699 // If not halo then state the info. to indicate that
2700 right_processor_plus_one[is] = 0;
2701 // Null this info.
2702 right_halo_element[is] = 0;
2703 // Null this info.
2704 right_haloed_element[is] = 0;
2705 }
2706
2707 } // for (is < nsegments). Used to get the halo info. of the
2708 // segments
2709
2710 // Now we have all the info. to be sent to the root processor and
2711 // compute the correct (global) boundary coordinates for the current
2712 // boundary
2713
2714 // The root processor will be in charge of performing the computing
2715 // of the coordinate values along the boundary, all the other
2716 // processors only send their info. and wait for receiving the new
2717 // starting values for each of its segments
2718
2719 // Choose the root processor
2720 const unsigned root_processor = 0;
2721 // ------------------------------------------------------------------
2722 // Starts the MPI stage
2723
2724 // The root processor receives the number of segments of each
2725 // processor associated to the current boundary
2726 Vector<unsigned> root_nsegments_per_processor(nproc);
2727 unsigned nsegments_mpi = nsegments;
2728 MPI_Gather(&nsegments_mpi,
2729 1,
2730 MPI_UNSIGNED,
2731 &root_nsegments_per_processor[0],
2732 1,
2733 MPI_UNSIGNED,
2734 root_processor,
2735 comm_pt->mpi_comm());
2736
2737 // Package the info. and prepare it to be sent
2738 // For the packaged info. we send 7 data per each segment, the indexes
2739 // are as follow; 0 left proc, 1 right proc, 2 left halo, 3 right
2740 // halo, 4 left haloed, 5 right haloed and 6 for nvertices per
2741 // segment
2742 // The size of the package (unsigned)
2743 const unsigned spu = 7;
2744 Vector<unsigned> flat_packed_unsigned_send_data(nsegments * spu);
2745 for (unsigned is = 0; is < nsegments; is++)
2746 {
2747 flat_packed_unsigned_send_data[(spu * is) + 0] =
2748 left_processor_plus_one[is];
2749 flat_packed_unsigned_send_data[(spu * is) + 1] =
2750 right_processor_plus_one[is];
2751 flat_packed_unsigned_send_data[(spu * is) + 2] = left_halo_element[is];
2752 flat_packed_unsigned_send_data[(spu * is) + 3] = right_halo_element[is];
2753 flat_packed_unsigned_send_data[(spu * is) + 4] = left_haloed_element[is];
2754 flat_packed_unsigned_send_data[(spu * is) + 5] = right_haloed_element[is];
2755 flat_packed_unsigned_send_data[(spu * is) + 6] =
2756 nvertices_per_segment[is];
2757 }
2758
2759 // How many data will this processor send
2760 const unsigned nudata_to_send = flat_packed_unsigned_send_data.size();
2761
2762 // How many data does the root processor will receive from each
2763 // processor
2764 Vector<int> root_nudata_to_receive(nproc, 0);
2765 // Total number of data to receive from all processors
2766 unsigned root_nutotal_data_receive = 0;
2767 for (unsigned ip = 0; ip < nproc; ip++)
2768 {
2769 // Compute the number of data the root processor will receive from
2770 // each processor
2771 root_nudata_to_receive[ip] = root_nsegments_per_processor[ip] * spu;
2772 // Add on the total number of data to receive
2773 root_nutotal_data_receive += root_nudata_to_receive[ip];
2774 }
2775
2776 // Stores and compute the offsets (in root) for the data received
2777 // from each processor
2778 Vector<int> root_uoffsets_receive(nproc, 0);
2779 root_uoffsets_receive[0] = 0;
2780 for (unsigned ip = 1; ip < nproc; ip++)
2781 {
2782 // Compute the offset to store the values from each processor
2783 root_uoffsets_receive[ip] =
2784 root_uoffsets_receive[ip - 1] + root_nudata_to_receive[ip - 1];
2785 }
2786
2787 // Create at least one entry so we don't get a seg fault below
2788 if (flat_packed_unsigned_send_data.size() == 0)
2789 {
2790 flat_packed_unsigned_send_data.resize(1);
2791 }
2792
2793 // Vector where to receive the info.
2794 Vector<unsigned> flat_packed_unsigned_receive_data(
2795 root_nutotal_data_receive);
2796 if (my_rank != root_processor)
2797 {
2798 // Create at least one entry so we don't get a seg fault below
2799 if (flat_packed_unsigned_receive_data.size() == 0)
2800 {
2801 flat_packed_unsigned_receive_data.resize(1);
2802 }
2803 } // if (my_rank!=root_processor)
2804
2805 MPI_Gatherv(&flat_packed_unsigned_send_data[0], // Flat package to
2806 // send info. from
2807 // each processor
2808 nudata_to_send, // Total number of data to send from
2809 // each processor
2810 MPI_UNSIGNED,
2811 &flat_packed_unsigned_receive_data[0], // Container
2812 // where to
2813 // receive the
2814 // info. from all
2815 // the processors
2816 &root_nudata_to_receive[0], // Number of data to receive
2817 // from each processor
2818 &root_uoffsets_receive[0], // The offset to store the
2819 // info. from each processor
2820 MPI_UNSIGNED,
2821 root_processor, // The processor that receives all the
2822 // info.
2823 comm_pt->mpi_comm());
2824
2825 // Clear the flat package to send
2826 flat_packed_unsigned_send_data.clear();
2827 flat_packed_unsigned_send_data.resize(0);
2828
2829 // Package the info. and prepare it to be sent
2830 // For the packaged info. we send 1 data per each segment which is
2831 // at the moment the arclength of each segment
2832 // The size of the package
2833 const unsigned spd = 1;
2834 Vector<double> flat_packed_double_send_data(nsegments * spd);
2835 for (unsigned is = 0; is < nsegments; is++)
2836 {
2837 flat_packed_double_send_data[(spd * is) + 0] = segment_arclength[is];
2838 }
2839
2840 // How many data will this processor send
2841 const unsigned nddata_to_send = flat_packed_double_send_data.size();
2842 // How many data does the root processor will receive from each
2843 // processor
2844 Vector<int> root_nddata_to_receive(nproc, 0);
2845 // Total number of data to receive from all processors
2846 unsigned root_ndtotal_data_receive = 0;
2847 for (unsigned ip = 0; ip < nproc; ip++)
2848 {
2849 root_nddata_to_receive[ip] = root_nsegments_per_processor[ip] * spd;
2850 root_ndtotal_data_receive += root_nddata_to_receive[ip];
2851 }
2852
2853 // Stores and compute the offsets for the data received from each
2854 // processor
2855 Vector<int> root_doffsets_receive(nproc, 0);
2856 root_doffsets_receive[0] = 0;
2857 for (unsigned ip = 1; ip < nproc; ip++)
2858 {
2859 // Compute the offset to store the values from each processor
2860 root_doffsets_receive[ip] =
2861 root_doffsets_receive[ip - 1] + root_nddata_to_receive[ip - 1];
2862 }
2863
2864 // Create at least one entry so we don't get a seg fault below
2865 if (flat_packed_double_send_data.size() == 0)
2866 {
2867 flat_packed_double_send_data.resize(1);
2868 }
2869
2870 // Vector where to receive the info.
2871 Vector<double> flat_packed_double_receive_data(root_ndtotal_data_receive);
2872 if (my_rank != root_processor)
2873 {
2874 // Create at least one entry so we don't get a seg fault below
2875 if (flat_packed_double_receive_data.size() == 0)
2876 {
2877 flat_packed_double_receive_data.resize(1);
2878 }
2879 }
2880
2881 MPI_Gatherv(&flat_packed_double_send_data[0], // Flat package to
2882 // send info. from
2883 // each processor
2884 nddata_to_send, // Total number of data to send from
2885 // each processor
2886 MPI_DOUBLE,
2887 &flat_packed_double_receive_data[0], // Container where
2888 // to receive the
2889 // info. from all
2890 // the processors
2891 &root_nddata_to_receive[0], // Number of data to receive
2892 // from each processor
2893 &root_doffsets_receive[0], // The offset to store the
2894 // info. from each processor
2895 MPI_DOUBLE,
2896 root_processor, // The processor that receives all the
2897 // info.
2898 comm_pt->mpi_comm());
2899
2900 // Clear the flat package to send
2901 flat_packed_double_send_data.clear();
2902 flat_packed_double_send_data.resize(0);
2903
2904 // The next three containers are only used by the root processor at
2905 // the end of its computations but it is necessary that all the
2906 // processors know them when calling back the info.
2907
2908 // Container that state the initial arclength for each segments
2909 // of each processor
2910 Vector<Vector<double>> root_initial_segment_arclength(nproc);
2911
2912 // Container that state the number of vertices before each segment
2913 // in a given processor
2914 Vector<Vector<unsigned>> root_nvertices_before_segment(nproc);
2915
2916 // The root processor needs to tell the other processor if it was
2917 // necessary to reverse a segment. Each processor should therefore
2918 // invert the face elements that compose every segment that was
2919 // inverted by the root processor
2920 Vector<Vector<unsigned>> root_segment_inverted(nproc);
2921
2922 // Used to store the accumulated arclength, used at the end of
2923 // communications to store the total arclength
2924 double root_accumulated_arclength = 0.0;
2925
2926 // Store the accumulated number of vertices, it means the total number
2927 // of vertices before each segment (counter)
2928 unsigned root_accumulated_vertices_before_segment = 0;
2929
2930 // The root processor is in charge of performing the connections
2931 // of the segments that define the complete boundary
2932 if (my_rank == root_processor)
2933 {
2934 // From the flat packaged received data re-create the data
2935 // structures storing the info. regarding the connectivity of the
2936 // segments, the number of vertices per segment and the local
2937 // arclength of each segment
2938
2939 // Stores the "rank" of the processor to the left of each segment,
2940 // zero if there is no processor to the left which states that the
2941 // segment is the first one on the boundary
2942 Vector<Vector<unsigned>> root_left_processor_plus_one(nproc);
2943
2944 // Stores the "rank" of the processor to the right of each segment,
2945 // zero if there is no processor to the right which states that the
2946 // segment is the last one on the boundary
2947 Vector<Vector<unsigned>> root_right_processor_plus_one(nproc);
2948
2949 // The id. of the halo element to the left of the segment, note that
2950 // this info. is not necessary if there is no processor to the left
2951 // of the segment or if the processor has no info about the boundary
2952 Vector<Vector<unsigned>> root_left_halo_element(nproc);
2953
2954 // The id. of the halo element to the right of the segment, note
2955 // that this info. is not necessary if there is no processor to
2956 // the right of the segment or if the processor has no info about
2957 // the boundary
2958 Vector<Vector<unsigned>> root_right_halo_element(nproc);
2959
2960 // The id. of the haloed element to the left of the segment, note
2961 // that this info. is not necessary if there is no processor to
2962 // the left of the segment or if the processor has no info about
2963 // the boundary
2964 Vector<Vector<unsigned>> root_left_haloed_element(nproc);
2965
2966 // The id. of the haloed element to the right of the segment, note
2967 // that this info. is not necessary if there is no processor to the
2968 // right of the segment or if the processor has no info about the
2969 // boundary
2970 Vector<Vector<unsigned>> root_right_haloed_element(nproc);
2971
2972 // The number of vertices per segment in each processor
2973 Vector<Vector<unsigned>> root_nvertices_per_segment(nproc);
2974
2975 // The arclength of each of the segments in the processors
2976 Vector<Vector<double>> root_segment_arclength(nproc);
2977
2978 unsigned ucounter = 0;
2979 unsigned dcounter = 0;
2980 for (unsigned ip = 0; ip < nproc; ip++)
2981 {
2982 // Get the number of segments in the current processor
2983 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
2984
2985 root_left_processor_plus_one[ip].resize(nsegs_iproc);
2986 root_right_processor_plus_one[ip].resize(nsegs_iproc);
2987 root_left_halo_element[ip].resize(nsegs_iproc);
2988 root_right_halo_element[ip].resize(nsegs_iproc);
2989 root_left_haloed_element[ip].resize(nsegs_iproc);
2990 root_right_haloed_element[ip].resize(nsegs_iproc);
2991
2992 // Additional info.
2993 root_nvertices_per_segment[ip].resize(nsegs_iproc);
2994 root_segment_arclength[ip].resize(nsegs_iproc);
2995 root_segment_inverted[ip].resize(nsegs_iproc);
2996
2997 // Extract the info. from the BIG package received from all
2998 // processors
2999 for (unsigned is = 0; is < nsegs_iproc; is++)
3000 {
3001 // ------ The flat unsigned package ------
3002 root_left_processor_plus_one[ip][is] =
3003 flat_packed_unsigned_receive_data[ucounter++];
3004 root_right_processor_plus_one[ip][is] =
3005 flat_packed_unsigned_receive_data[ucounter++];
3006 root_left_halo_element[ip][is] =
3007 flat_packed_unsigned_receive_data[ucounter++];
3008 root_right_halo_element[ip][is] =
3009 flat_packed_unsigned_receive_data[ucounter++];
3010 root_left_haloed_element[ip][is] =
3011 flat_packed_unsigned_receive_data[ucounter++];
3012 root_right_haloed_element[ip][is] =
3013 flat_packed_unsigned_receive_data[ucounter++];
3014 root_nvertices_per_segment[ip][is] =
3015 flat_packed_unsigned_receive_data[ucounter++];
3016
3017 // ------ The flat double package ------
3018 root_segment_arclength[ip][is] =
3019 flat_packed_double_receive_data[dcounter++];
3020 } // for (is < nsegs_iproc)
3021 } // for (ip < nproc)
3022
3023 // Now the root processor has all the info. to find out the
3024 // CONNECTIVITY of the segments in each processor
3025
3026 // Container that stores the info. related with the connectivity
3027 // of the segments of each processor
3028 Vector<Vector<int>> left_connected_segment_plus_one(nproc);
3029 Vector<Vector<int>> right_connected_segment_plus_one(nproc);
3030 for (unsigned ip = 0; ip < nproc; ip++)
3031 {
3032 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3033 left_connected_segment_plus_one[ip].resize(nsegs_iproc, -1);
3034 right_connected_segment_plus_one[ip].resize(nsegs_iproc, -1);
3035 } // for (ip < nprocs)
3036
3037 // In charge of storing the connectivity of the segments, the pair
3038 // indicates the processor and the segment number
3039 std::list<std::pair<unsigned, unsigned>> proc_seg_connectivity;
3040 proc_seg_connectivity.clear();
3041
3042 // Done segments on processor
3043 std::map<std::pair<unsigned, unsigned>, bool> done_segment;
3044
3045 // Take the first segment of the first processor with segments and
3046 // add it to the list of segments
3047 unsigned left_proc = 0;
3048 unsigned right_proc = 0;
3049 unsigned left_seg = 0;
3050 unsigned right_seg = 0;
3051 for (unsigned ip = 0; ip < nproc; ip++)
3052 {
3053 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3054 if (nsegs_iproc > 0)
3055 {
3056 right_proc = left_proc = ip;
3057 right_seg = left_seg = 0;
3058 break; // Break because it is the first processor with at
3059 // least one segment
3060 }
3061 } // for (ip < nproc)
3062
3063 // ... and add it to the list of segments
3064 std::pair<unsigned, unsigned> add_segment =
3065 std::make_pair(left_proc, left_seg);
3066 done_segment[add_segment] = true;
3067 proc_seg_connectivity.push_back(add_segment);
3068
3069 // Flags to indicate when a segment was added to the left or right
3070 // of the current list of segments
3071 bool added_segment_to_the_left = false;
3072 bool added_segment_to_the_right = false;
3073
3074 do // while(added_segment_to_the_left || added_segment_to_the_right)
3075 {
3076 // Read the left-most processor and segment in the list
3077 std::pair<unsigned, unsigned> left_pair = proc_seg_connectivity.front();
3078 left_proc = left_pair.first;
3079 left_seg = left_pair.second;
3080
3081 // Get the processor number to the left of the left-most
3082 // segment in the list
3083 const unsigned new_left_proc =
3084 root_left_processor_plus_one[left_proc][left_seg];
3085
3086 if (new_left_proc != 0)
3087 {
3088 // Initialise flag
3089 added_segment_to_the_left = false;
3090 // Get the left halo element id
3091 const unsigned left_halo_id =
3092 root_left_halo_element[left_proc][left_seg];
3093
3094 // Get the left haloed element id
3095 const unsigned left_haloed_id =
3096 root_left_haloed_element[left_proc][left_seg];
3097
3098 // Go through the segments on the new left processor and look
3099 // for the corresponding left_halo_id in the haloed_ids
3100 const unsigned nsegs_new_left_proc =
3101 root_nsegments_per_processor[new_left_proc - 1];
3102
3103 for (unsigned ils = 0; ils < nsegs_new_left_proc; ils++)
3104 {
3105 std::pair<unsigned, unsigned> candidate_seg =
3106 std::make_pair(new_left_proc - 1, ils);
3107
3108 // Check that the segment has not been already added
3109 if (!done_segment[candidate_seg])
3110 {
3111 // Only consider the segments on new left processor which
3112 // right processor is the current one (left_proc)
3113 const unsigned right_proc_of_new_left_proc =
3114 root_right_processor_plus_one[new_left_proc - 1][ils];
3115 // Also get the left_proc_of_new_left_proc (in case that it
3116 // be necessary to invert the segment)
3117 const unsigned left_proc_of_new_left_proc =
3118 root_left_processor_plus_one[new_left_proc - 1][ils];
3119 // Check the not inverted case (to the left and not
3120 // inverted)
3121 if (right_proc_of_new_left_proc != 0 &&
3122 right_proc_of_new_left_proc - 1 == left_proc)
3123 {
3124 // Get the haloed/haloed element id of the current segment
3125 // in the new left processor and compare it to the
3126 // halo/haloed element id of the left_processor
3127 const unsigned right_halo_id =
3128 root_right_halo_element[new_left_proc - 1][ils];
3129 const unsigned right_haloed_id =
3130 root_right_haloed_element[new_left_proc - 1][ils];
3131 if (left_halo_id == right_haloed_id &&
3132 left_haloed_id == right_halo_id)
3133 {
3134 // We have a match of the segments (store the segment
3135 // number plus one on the processor to the left)
3136 left_connected_segment_plus_one[left_proc][left_seg] =
3137 ils + 1;
3138 // Add the pair to the connectivity list
3139 proc_seg_connectivity.push_front(candidate_seg);
3140 added_segment_to_the_left = true;
3141 break;
3142 }
3143 } // if (right_proc_of_new_left_proc-1 == left_proc)
3144
3145 // Check the inverted case (to the left and inverted)
3146 if (left_proc_of_new_left_proc != 0 &&
3147 left_proc_of_new_left_proc - 1 == left_proc)
3148 {
3149 // Get the haloed element id of the current segment
3150 // (inverted version) in the new left processor and
3151 // compare it to the halo element id of the left_processor
3152 const unsigned inv_left_halo_id =
3153 root_left_halo_element[new_left_proc - 1][ils];
3154 const unsigned inv_left_haloed_id =
3155 root_left_haloed_element[new_left_proc - 1][ils];
3156 if (left_halo_id == inv_left_haloed_id &&
3157 left_haloed_id == inv_left_halo_id)
3158 {
3159 // We have a match of the segments (store the segment
3160 // number plus one on the processor to the left)
3161 left_connected_segment_plus_one[left_proc][left_seg] =
3162 ils + 1;
3163 // Add the pair to the connectivity list
3164 proc_seg_connectivity.push_front(candidate_seg);
3165
3166 // In addition to the connectivity we need to invert the
3167 // segment (the information)
3168 const unsigned tmp_proc =
3169 root_left_processor_plus_one[new_left_proc - 1][ils];
3170 const unsigned tmp_halo =
3171 root_left_halo_element[new_left_proc - 1][ils];
3172 const unsigned tmp_haloed =
3173 root_left_haloed_element[new_left_proc - 1][ils];
3174
3175 root_left_processor_plus_one[new_left_proc - 1][ils] =
3176 root_right_processor_plus_one[new_left_proc - 1][ils];
3177 root_left_halo_element[new_left_proc - 1][ils] =
3178 root_right_halo_element[new_left_proc - 1][ils];
3179 root_left_haloed_element[new_left_proc - 1][ils] =
3180 root_right_haloed_element[new_left_proc - 1][ils];
3181
3182 root_right_processor_plus_one[new_left_proc - 1][ils] =
3183 tmp_proc;
3184 root_right_halo_element[new_left_proc - 1][ils] = tmp_halo;
3185 root_right_haloed_element[new_left_proc - 1][ils] =
3186 tmp_haloed;
3187
3188 // ... and mark the segment as inverted in the root
3189 // processor to inform back to the owner processor
3190 root_segment_inverted[new_left_proc - 1][ils] = 1;
3191
3192 added_segment_to_the_left = true;
3193 break;
3194 }
3195 } // if (left_proc_of_new_left_proc-1 == left_proc)
3196 } // if (!done_segment[candidate_segment])
3197 } // for (ils < nsegs_new_left_proc)
3198
3199#ifdef PARANOID
3200 if (!added_segment_to_the_left)
3201 {
3202 std::ostringstream error_message;
3203 error_message
3204 << "The corresponding processor and segment to the left of "
3205 << "the current left\nmost segment was not found\n";
3206 throw OomphLibError(error_message.str(),
3207 "TriangleMesh::compute_boundary_segments_"
3208 "connectivity_and_initial_zeta_values()",
3209 OOMPH_EXCEPTION_LOCATION);
3210 }
3211#endif
3212 } // if (new_left_proc != 0)
3213 else
3214 {
3215 // No more segments to the left
3216 added_segment_to_the_left = false;
3217 }
3218
3219 // Read the info. of the right processor and the right segment
3220 std::pair<unsigned, unsigned> right_pair = proc_seg_connectivity.back();
3221 right_proc = right_pair.first;
3222 right_seg = right_pair.second;
3223
3224 // Get the processor number to the right of the right-most
3225 // segment in the list
3226 const unsigned new_right_proc =
3227 root_right_processor_plus_one[right_proc][right_seg];
3228
3229 if (new_right_proc != 0)
3230 {
3231 // Initialise flag
3232 added_segment_to_the_right = false;
3233 // Get the right halo element id
3234 const unsigned right_halo_id =
3235 root_right_halo_element[right_proc][right_seg];
3236
3237 // Get the right halo element id
3238 const unsigned right_haloed_id =
3239 root_right_haloed_element[right_proc][right_seg];
3240
3241 // Go through the segments on the new right processor and look
3242 // for the corresponding right_halo_id in the haloed_ids
3243 const unsigned nsegs_new_right_proc =
3244 root_nsegments_per_processor[new_right_proc - 1];
3245
3246 for (unsigned irs = 0; irs < nsegs_new_right_proc; irs++)
3247 {
3248 std::pair<unsigned, unsigned> candidate_seg =
3249 std::make_pair(new_right_proc - 1, irs);
3250
3251 // Check that the segment has not been already added
3252 if (!done_segment[candidate_seg])
3253 {
3254 // Only consider the segments on new right processor which
3255 // left processor is the current one (right_proc)
3256 const unsigned left_proc_of_new_right_proc =
3257 root_left_processor_plus_one[new_right_proc - 1][irs];
3258 // Also get the right_proc_of_new_right_proc (in case
3259 // that it be necessary to invert the segment)
3260 const unsigned right_proc_of_new_right_proc =
3261 root_right_processor_plus_one[new_right_proc - 1][irs];
3262 // Check the not inverted case (to the right and not
3263 // inverted)
3264 if (left_proc_of_new_right_proc != 0 &&
3265 left_proc_of_new_right_proc - 1 == right_proc)
3266 {
3267 // Get the haloed element id of the current segment in the
3268 // new right processor and compare it to the halo element
3269 // id of the right_processor
3270 const unsigned left_halo_id =
3271 root_left_halo_element[new_right_proc - 1][irs];
3272 const unsigned left_haloed_id =
3273 root_left_haloed_element[new_right_proc - 1][irs];
3274
3275 if (right_halo_id == left_haloed_id &&
3276 right_haloed_id == left_halo_id)
3277 {
3278 // We have a match of the segments (store the segment
3279 // number plus one on the processor to the right)
3280 right_connected_segment_plus_one[right_proc][right_seg] =
3281 irs + 1;
3282 // Add the connectivity information to the list
3283 proc_seg_connectivity.push_back(candidate_seg);
3284 added_segment_to_the_right = true;
3285 break;
3286 }
3287 } // if (left_proc_of_new_right_proc-1 == right_proc)
3288
3289 // Check the inverted case (to the right and inverted)
3290 if (right_proc_of_new_right_proc != 0 &&
3291 right_proc_of_new_right_proc - 1 == right_proc)
3292 {
3293 // Get the haloed element id of the current segment
3294 // (inverted version) in the new right processor and
3295 // compare it to the halo element id of the
3296 // right_processor
3297 const unsigned inv_right_halo_id =
3298 root_right_halo_element[new_right_proc - 1][irs];
3299 const unsigned inv_right_haloed_id =
3300 root_right_haloed_element[new_right_proc - 1][irs];
3301 if (right_halo_id == inv_right_haloed_id &&
3302 right_haloed_id == inv_right_halo_id)
3303 {
3304 // We have a match of the segments (store the segment
3305 // number plus one on the processor to the right)
3306 right_connected_segment_plus_one[right_proc][right_seg] =
3307 irs + 1;
3308 // Add the connectivity information to the list
3309 proc_seg_connectivity.push_back(candidate_seg);
3310 // In addition to the connectivity we need to invert the
3311 // segment
3312 const unsigned tmp_proc =
3313 root_left_processor_plus_one[new_right_proc - 1][irs];
3314 const unsigned tmp_halo =
3315 root_left_halo_element[new_right_proc - 1][irs];
3316 const unsigned tmp_haloed =
3317 root_left_haloed_element[new_right_proc - 1][irs];
3318
3319 root_left_processor_plus_one[new_right_proc - 1][irs] =
3320 root_right_processor_plus_one[new_right_proc - 1][irs];
3321 root_left_halo_element[new_right_proc - 1][irs] =
3322 root_right_halo_element[new_right_proc - 1][irs];
3323 root_left_haloed_element[new_right_proc - 1][irs] =
3324 root_right_haloed_element[new_right_proc - 1][irs];
3325
3326 root_right_processor_plus_one[new_right_proc - 1][irs] =
3327 tmp_proc;
3328 root_right_halo_element[new_right_proc - 1][irs] = tmp_halo;
3329 root_right_haloed_element[new_right_proc - 1][irs] =
3330 tmp_haloed;
3331
3332 // ... and mark the segment as inverted in the root
3333 // processor to inform back to the owner processor
3334 root_segment_inverted[new_right_proc - 1][irs] = 1;
3335
3336 added_segment_to_the_right = true;
3337 break;
3338 }
3339 } // if (right_proc_of_new_right_proc-1 == right_proc)
3340 } // if (!done_segment[candidate_segment])
3341 } // for (irs < nsegs_new_left_proc)
3342
3343#ifdef PARANOID
3344 if (!added_segment_to_the_right)
3345 {
3346 std::ostringstream error_message;
3347 error_message
3348 << "The corresponding processor and segment to the right of "
3349 << "the current right\nmost segment was not found\n";
3350 throw OomphLibError(error_message.str(),
3351 "TriangleMesh::compute_boundary_segments_"
3352 "connectivity_and_initial_zeta_values()",
3353 OOMPH_EXCEPTION_LOCATION);
3354 }
3355#endif
3356 } // if (new_right_proc != 0)
3357 else
3358 {
3359 // No more segments to the left
3360 added_segment_to_the_right = false;
3361 }
3362
3363 } while (added_segment_to_the_left || added_segment_to_the_right);
3364
3365 // Once we have connected the segments then we can compute the
3366 // initial and final zeta values based on the arclength of each
3367 // individual segment
3368
3369 // Get the total number of segments, which MUST be the same as the
3370 // total number of segments in all processors
3371 const unsigned ntotal_segments = proc_seg_connectivity.size();
3372#ifdef PARANOID
3373 unsigned tmp_total_segments = 0;
3374 for (unsigned ip = 0; ip < nproc; ip++)
3375 {
3376 tmp_total_segments += root_nsegments_per_processor[ip];
3377 }
3378
3379 // Check that the total number of segments in all processors is
3380 // the same as the number of segments that form the boundary
3381 if (ntotal_segments != tmp_total_segments)
3382 {
3383 std::ostringstream error_message;
3384 error_message << "The number of sorted segments (" << ntotal_segments
3385 << ") on "
3386 << "boundary (" << b
3387 << ")\nis different from the total number of "
3388 << "segments (" << tmp_total_segments
3389 << ") in all\nprocessors.\n\n";
3390 throw OomphLibError(error_message.str(),
3391 OOMPH_CURRENT_FUNCTION,
3392 OOMPH_EXCEPTION_LOCATION);
3393 } // if (ntotal_segments!=tmp_total_segments)
3394#endif
3395
3396 // Now that we know the connectivity of the segments we can
3397 // compute the initial arclength of each segment in the
3398 // processors. Additionally we also get the number of vertices
3399 // before each of the segments. Resize the containers considering
3400 // the number of segments in each processor
3401 for (unsigned ip = 0; ip < nproc; ip++)
3402 {
3403 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3404 root_initial_segment_arclength[ip].resize(nsegs_iproc);
3405 root_nvertices_before_segment[ip].resize(nsegs_iproc);
3406 }
3407
3408 Vector<double> aux_initial_segment_arclength(ntotal_segments);
3409 Vector<unsigned> aux_nvertices_before_segment(ntotal_segments);
3410
3411 ucounter = 0;
3412 for (std::list<std::pair<unsigned, unsigned>>::iterator it_list =
3413 proc_seg_connectivity.begin();
3414 it_list != proc_seg_connectivity.end();
3415 it_list++)
3416 {
3417 const unsigned iproc = static_cast<unsigned>((*it_list).first);
3418 const unsigned iseg = static_cast<unsigned>((*it_list).second);
3419 const double iseg_arclength = root_segment_arclength[iproc][iseg];
3420 const unsigned iseg_nvertices = root_nvertices_per_segment[iproc][iseg];
3421
3422 aux_initial_segment_arclength[ucounter] = root_accumulated_arclength;
3423 aux_nvertices_before_segment[ucounter] =
3424 root_accumulated_vertices_before_segment;
3425
3426 // Set the initial zeta value for the segment
3427 root_initial_segment_arclength[iproc][iseg] =
3428 root_accumulated_arclength;
3429 // Set the number of vertices before the current segment
3430 root_nvertices_before_segment[iproc][iseg] =
3431 root_accumulated_vertices_before_segment;
3432
3433 // Add the arclength of the segment to the global arclength
3434 root_accumulated_arclength += iseg_arclength;
3435 // Add the number of vertices to the global number of vertices
3436 root_accumulated_vertices_before_segment += iseg_nvertices - 1;
3437
3438 // Increase the counter
3439 ucounter++;
3440 } // for (loop over the sorted segments to assigne initial
3441 // arlength and initial number of vertices)
3442
3443 // Increase by one to get the total number of vertices on the
3444 // boundary
3445 root_accumulated_vertices_before_segment++;
3446
3447 // Get the processors with the initial and final segment.
3448 proc_with_initial_seg = proc_seg_connectivity.front().first;
3449 proc_with_final_seg = proc_seg_connectivity.back().first;
3450 // Also get the corresponding initial and final segment indexes
3451 // (on the initial and final processors)
3452 initial_segment = proc_seg_connectivity.front().second;
3453 final_segment = proc_seg_connectivity.back().second;
3454
3455 } // if (my_rank == root_processor)
3456
3457 // Get the total number of segments
3458 unsigned root_ntotal_segments = 0;
3459 for (unsigned ip = 0; ip < nproc; ip++)
3460 {
3461 root_ntotal_segments += root_nsegments_per_processor[ip];
3462 }
3463
3464 // Package the info. that will be sent to each processor. For the
3465 // unsigned package we send the number of vertices before each
3466 // segment in each processor and whether it was inverted or not
3467 // Package size
3468 const unsigned rspu = 2;
3469 flat_packed_unsigned_send_data.clear();
3470 flat_packed_unsigned_send_data.resize(root_ntotal_segments * rspu);
3471 unsigned ucounter = 0;
3472 // Collect the info. from all the segments in the processors
3473 for (unsigned ip = 0; ip < nproc; ip++)
3474 {
3475 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3476 for (unsigned is = 0; is < nsegs_iproc; is++)
3477 {
3478 flat_packed_unsigned_send_data[ucounter++] =
3479 root_nvertices_before_segment[ip][is];
3480 flat_packed_unsigned_send_data[ucounter++] =
3481 root_segment_inverted[ip][is];
3482 } // for (is < nsegs_iproc)
3483 } // for (ip < nproc)
3484
3485 // How many data does the root processor will send to each processor
3486 Vector<int> root_nudata_to_send(nproc, 0);
3487 for (unsigned ip = 0; ip < nproc; ip++)
3488 {
3489 // Get the number of data to send to ip processor
3490 root_nudata_to_send[ip] = root_nsegments_per_processor[ip] * rspu;
3491 }
3492
3493 // Store and compute the offsets for the data sent to each processor
3494 Vector<int> root_uoffsets_send(nproc, 0);
3495 root_uoffsets_send[0] = 0;
3496 for (unsigned ip = 1; ip < nproc; ip++)
3497 {
3498 // Compute the offset to send the values to each processor
3499 root_uoffsets_send[ip] =
3500 root_uoffsets_send[ip - 1] + root_nudata_to_send[ip - 1];
3501 }
3502
3503 // Number of data to receive from root
3504 unsigned nutotal_data_receive = nsegments * rspu;
3505
3506 if (my_rank != root_processor)
3507 {
3508 // Create at least one entry so we don't get a seg fault below
3509 if (flat_packed_unsigned_send_data.size() == 0)
3510 {
3511 flat_packed_unsigned_send_data.resize(1);
3512 }
3513 }
3514
3515 // Clear and resize the vector where to receive the info.
3516 flat_packed_unsigned_receive_data.clear();
3517 flat_packed_unsigned_receive_data.resize(nutotal_data_receive);
3518 // Create at least one entry so we don't get a seg fault below
3519 if (flat_packed_unsigned_receive_data.size() == 0)
3520 {
3521 flat_packed_unsigned_receive_data.resize(1);
3522 }
3523
3524 MPI_Scatterv(&flat_packed_unsigned_send_data[0],
3525 &root_nudata_to_send[0],
3526 &root_uoffsets_send[0],
3527 MPI_UNSIGNED,
3528 &flat_packed_unsigned_receive_data[0],
3529 nutotal_data_receive,
3530 MPI_UNSIGNED,
3531 root_processor,
3532 comm_pt->mpi_comm());
3533
3534 // Package the info. that will be sent to each processor, for the
3535 // double package we send (one data per segment) the initial
3536 // arclength for each segment
3537 const unsigned rspd = 1;
3538 flat_packed_double_send_data.clear();
3539 flat_packed_double_send_data.resize(root_ntotal_segments * rspd);
3540 unsigned dcounter = 0;
3541 // Collect the info. from all the segments in the processors
3542 for (unsigned ip = 0; ip < nproc; ip++)
3543 {
3544 const unsigned nsegs_iproc = root_nsegments_per_processor[ip];
3545 for (unsigned is = 0; is < nsegs_iproc; is++)
3546 {
3547 flat_packed_double_send_data[dcounter++] =
3548 root_initial_segment_arclength[ip][is];
3549 }
3550 }
3551
3552 // How many data does the root processor will send to each processor
3553 Vector<int> root_nddata_to_send(nproc, 0);
3554 for (unsigned ip = 0; ip < nproc; ip++)
3555 {
3556 // Number of data send to ip processor
3557 root_nddata_to_send[ip] = root_nsegments_per_processor[ip] * rspd;
3558 }
3559
3560 // Store and compute the offsets for the data sent to each processor
3561 Vector<int> root_doffsets_send(nproc, 0);
3562 root_doffsets_send[0] = 0;
3563 for (unsigned ip = 1; ip < nproc; ip++)
3564 {
3565 // Compute the offset to send the values to each processor
3566 root_doffsets_send[ip] =
3567 root_doffsets_send[ip - 1] + root_nddata_to_send[ip - 1];
3568 }
3569
3570 // Number of double data to receive from root
3571 unsigned ndtotal_data_receive = nsegments * rspd;
3572
3573 if (my_rank != root_processor)
3574 {
3575 // Create at least one entry so we don't get a seg fault below
3576 if (flat_packed_double_send_data.size() == 0)
3577 {
3578 flat_packed_double_send_data.resize(1);
3579 }
3580 }
3581
3582 // Clear and resize the vector where to receive the info.
3583 flat_packed_double_receive_data.clear();
3584 flat_packed_double_receive_data.resize(ndtotal_data_receive);
3585 // Create at least one entry so we don't get a seg fault below
3586 if (flat_packed_double_receive_data.size() == 0)
3587 {
3588 flat_packed_double_receive_data.resize(1);
3589 }
3590
3591 MPI_Scatterv(&flat_packed_double_send_data[0],
3592 &root_nddata_to_send[0],
3593 &root_doffsets_send[0],
3594 MPI_DOUBLE,
3595 &flat_packed_double_receive_data[0],
3596 ndtotal_data_receive,
3597 MPI_DOUBLE,
3598 root_processor,
3599 comm_pt->mpi_comm());
3600
3601 // Read if the segments need to be inverted and read the initial
3602 // arclengths
3603 ucounter = 0;
3604 dcounter = 0;
3605
3606 // Read the info. from the flat package and store it in their
3607 // corresponding containers
3608 for (unsigned is = 0; is < nsegments; is++)
3609 {
3610 // The flat unsigned package
3611 nvertices_before_segment[is] =
3612 flat_packed_unsigned_receive_data[ucounter++];
3613 // The segment inverted flag
3614 segment_inverted[is] = flat_packed_unsigned_receive_data[ucounter++];
3615 // The flat double package
3616 initial_segment_arclength[is] =
3617 flat_packed_double_receive_data[dcounter++];
3618 } // for (is < nsegments)
3619
3620 // Perform two additional communications to get the total number of
3621 // vertices, the processors with the initial and final segments, the
3622 // corresponding initial and final segments ...
3623 const unsigned numore_info = 5;
3624 Vector<unsigned> flat_package_unsigned_more_info(numore_info);
3625 // Prepare the info ...
3626 flat_package_unsigned_more_info[0] =
3627 root_accumulated_vertices_before_segment;
3628 flat_package_unsigned_more_info[1] = proc_with_initial_seg;
3629 flat_package_unsigned_more_info[2] = proc_with_final_seg;
3630 flat_package_unsigned_more_info[3] = initial_segment;
3631 flat_package_unsigned_more_info[4] = final_segment;
3632
3633 // Send the info. to all processors
3634 MPI_Bcast(&flat_package_unsigned_more_info[0],
3635 numore_info,
3636 MPI_UNSIGNED,
3637 root_processor,
3638 comm_pt->mpi_comm());
3639
3640 // ... and store the info. in the proper containers
3641 root_accumulated_vertices_before_segment =
3642 flat_package_unsigned_more_info[0];
3643 proc_with_initial_seg = flat_package_unsigned_more_info[1];
3644 proc_with_final_seg = flat_package_unsigned_more_info[2];
3645 initial_segment = flat_package_unsigned_more_info[3];
3646 final_segment = flat_package_unsigned_more_info[4];
3647
3648 // Do the same for the maximum zeta value
3649 MPI_Bcast(&root_accumulated_arclength,
3650 1,
3651 MPI_DOUBLE,
3652 root_processor,
3653 comm_pt->mpi_comm());
3654
3655 // -----------------------------------------------------------------
3656 // Clear the storage to store the data that will be used by the
3657 // setup boundary coordinates method, if we do not perform the
3658 // cleaning then previous data from previous iterations will remain
3659 // there
3660 // -----------------------------------------------------------------
3661 // The info. for the boundary
3662 Boundary_initial_coordinate[b].clear();
3663 Boundary_final_coordinate[b].clear();
3664
3665 Boundary_initial_zeta_coordinate[b].clear();
3666 Boundary_final_zeta_coordinate[b].clear();
3667
3668 // The info. for the segments
3669 Boundary_segment_inverted[b].clear();
3670 Boundary_segment_initial_coordinate[b].clear();
3671 Boundary_segment_final_coordinate[b].clear();
3672
3673 Boundary_segment_initial_zeta[b].clear();
3674 Boundary_segment_final_zeta[b].clear();
3675
3676 Boundary_segment_initial_arclength[b].clear();
3677 Boundary_segment_final_arclength[b].clear();
3678
3679 // Now copy all the info. to the containers to be sent to any other
3680 // mesh (in the adaptation method)
3681 for (unsigned is = 0; is < nsegments; is++)
3682 {
3683 // At this point we can get the initial and final coordinates for
3684 // each segment
3685 Vector<double> first_seg_coord(2);
3686 Vector<double> last_seg_coord(2);
3687
3688 // In order to get the first and last coordinates of each segment we
3689 // first need to identify the first and last nonhalo element of each
3690 // segment, and then get the first and last node of the segment
3691
3692 // Get the first nonhalo face element on the segment
3693 FiniteElement* first_seg_ele_pt =
3694 segment_sorted_nonhalo_ele_pt[is].front();
3695
3696#ifdef PARANOID
3697 // Check if the face element is nonhalo, it shouldn't, but better
3698 // check
3699 if (first_seg_ele_pt->is_halo())
3700 {
3701 std::ostringstream error_message;
3702 error_message << "The first face element in the (" << is
3703 << ")-th segment is halo\n";
3704 throw OomphLibError(error_message.str(),
3705 "TriangleMesh::compute_boundary_segments_"
3706 "connectivity_and_initial_zeta_values()",
3707 OOMPH_EXCEPTION_LOCATION);
3708 } // if (tmp_first_bulk_ele_pt->is_halo())
3709#endif
3710
3711 // Number of nodes
3712 const unsigned nnod = first_seg_ele_pt->nnode();
3713
3714 // Get the first node of the current segment
3715 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
3716 if (is_inverted[first_seg_ele_pt])
3717 {
3718 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
3719 }
3720
3721 // Get the last nonhalo face element on the segment
3722 FiniteElement* last_seg_ele_pt = segment_sorted_nonhalo_ele_pt[is].back();
3723
3724#ifdef PARANOID
3725 // Check if the face element is nonhalo, it shouldn't, but better
3726 // check
3727 if (last_seg_ele_pt->is_halo())
3728 {
3729 std::ostringstream error_message;
3730 error_message << "The last face element in the (" << is
3731 << ")-th segment is halo\n";
3732 throw OomphLibError(error_message.str(),
3733 "TriangleMesh::compute_boundary_segments_"
3734 "connectivity_and_initial_zeta_values()",
3735 OOMPH_EXCEPTION_LOCATION);
3736 } // if (tmp_first_bulk_ele_pt->is_halo())
3737#endif
3738
3739 // Get the last node of the current segment
3740 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
3741 if (is_inverted[last_seg_ele_pt])
3742 {
3743 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
3744 }
3745
3746 // Get the coordinates for the first and last segment's node
3747 for (unsigned i = 0; i < 2; i++)
3748 {
3749 first_seg_coord[i] = first_seg_node_pt->x(i);
3750 last_seg_coord[i] = last_seg_node_pt->x(i);
3751 }
3752
3753 // -----------------------------------------------------------------
3754 // Copy the info. if the segment is inverted
3755 Boundary_segment_inverted[b].push_back(segment_inverted[is]);
3756
3757 // Check if the segment is inverted, if that is the case then invert
3758 // the first and last seg. coordinates
3759 if (!segment_inverted[is])
3760 {
3761 // Store the initial and final coordinates that will help to
3762 // identify the segments in the new meshes created from this one
3763 Boundary_segment_initial_coordinate[b].push_back(first_seg_coord);
3764 Boundary_segment_final_coordinate[b].push_back(last_seg_coord);
3765 }
3766 else
3767 {
3768 // Store the initial and final coordinates that will help to
3769 // identify the segments in the new meshes created from this one
3770 // Invert the initial and final coordinates
3771 Boundary_segment_initial_coordinate[b].push_back(last_seg_coord);
3772 Boundary_segment_final_coordinate[b].push_back(first_seg_coord);
3773 }
3774
3775 // Now assign initial and final zeta boundary coordinates for each
3776 // segment
3777 // -----------------------------------------------------------------
3778 // If there is a geom object then
3779 if (boundary_geom_object_pt(b) != 0)
3780 {
3781 // Store the initial and final zeta for the current segments (we
3782 // got this when we assigned arclength to the segments in the
3783 // current processor)
3784 if (segment_inverted[is])
3785 {
3786 Boundary_segment_initial_zeta[b].push_back(final_zeta_segment[is]);
3787 Boundary_segment_final_zeta[b].push_back(initial_zeta_segment[is]);
3788 }
3789 else
3790 {
3791 Boundary_segment_initial_zeta[b].push_back(initial_zeta_segment[is]);
3792 Boundary_segment_final_zeta[b].push_back(final_zeta_segment[is]);
3793 }
3794 } // if (boundary_geom_object_pt(b)!=0)
3795 else
3796 {
3797 // Store the initial arclength and vertices number for the
3798 // current segment
3799 Boundary_segment_initial_arclength[b].push_back(
3800 initial_segment_arclength[is]);
3801
3802 Boundary_segment_final_arclength[b].push_back(
3803 initial_segment_arclength[is] + segment_arclength[is]);
3804
3805 } // else if (boundary_geom_object_pt(b)!=0)
3806
3807 } // // for (is < nsegments)
3808
3809 // Get the number of segments from the sets of nodes
3810#ifdef PARANOID
3811 if (segment_all_nodes_pt.size() != nsegments)
3812 {
3813 std::ostringstream error_message;
3814 error_message << "The number of segments (" << nsegments
3815 << ") and the number of "
3816 << "set of nodes (" << segment_all_nodes_pt.size()
3817 << ") representing\n"
3818 << "the\nsegments is different!!!\n\n";
3819 throw OomphLibError(error_message.str(),
3820 "TriangleMesh::compute_boundary_segments_"
3821 "connectivity_and_initial_zeta_values()",
3822 OOMPH_EXCEPTION_LOCATION);
3823 }
3824#endif
3825
3826 // The nodes have been assigned arc-length coordinates from one end
3827 // or the other of the connected segment.
3828
3829 // -----------------------------------------------------------------
3830 // If mesh is distributed get the info. regarding the initial and
3831 // final nodes coordinates on the boundary, same as the zeta
3832 // boundary values for those nodes
3833
3834 // Storage for the coordinates of the first and last nodes on the
3835 // boundary
3836 Vector<double> first_coordinate(2);
3837 Vector<double> last_coordinate(2);
3838
3839 // Storage for the zeta coordinate of the first and last nodes on
3840 // the boundary
3841 Vector<double> first_node_zeta_coordinate(1, 0.0);
3842 Vector<double> last_node_zeta_coordinate(1, 0.0);
3843
3844 // Send three data to all processors, the x[0], x[1] coordinate and
3845 // the zeta coordinate
3846 const unsigned ndtotal_data = 3;
3847 Vector<double> flat_packed_double_data_initial_seg(ndtotal_data);
3848
3849 // If the mesh is distributed then check if this processor has the
3850 // initial segment
3851 if (my_rank == proc_with_initial_seg)
3852 {
3853 // Stores the firts element of the segment
3854 FiniteElement* first_ele_pt = 0;
3855 // Stores the first node of the boundary
3856 Node* first_node_pt = 0;
3857 // Check if the segment is inverted
3858 if (!segment_inverted[initial_segment])
3859 {
3860 // Get access to the first element on the segment marked as
3861 // initial
3862 first_ele_pt = segment_sorted_ele_pt[initial_segment].front();
3863
3864 // Number of nodes
3865 const unsigned nnod = first_ele_pt->nnode();
3866
3867 // Get the first node of the current segment
3868 first_node_pt = first_ele_pt->node_pt(0);
3869 if (is_inverted[first_ele_pt])
3870 {
3871 first_node_pt = first_ele_pt->node_pt(nnod - 1);
3872 }
3873 } // if (!segment_inverted[initial_segment])
3874 else
3875 {
3876 // Get access to the first element on the segment marked as
3877 // initial
3878 first_ele_pt = segment_sorted_ele_pt[initial_segment].back();
3879
3880 // Number of nodes
3881 const unsigned nnod = first_ele_pt->nnode();
3882
3883 // Get the first node of the current segment
3884 first_node_pt = first_ele_pt->node_pt(nnod - 1);
3885 if (is_inverted[first_ele_pt])
3886 {
3887 first_node_pt = first_ele_pt->node_pt(0);
3888 }
3889 } // else if (!segment_inverted[initial_segment])
3890
3891 // Get the coordinates for the first node
3892 for (unsigned i = 0; i < 2; i++)
3893 {
3894 flat_packed_double_data_initial_seg[i] = first_node_pt->x(i);
3895 }
3896
3897 // Get the zeta coordinates for the first node
3898 Vector<double> tmp_zeta(1);
3899 first_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3900
3901 // If there is a geometric object associated to the boundary then
3902 // further process is necessary
3903 if (this->boundary_geom_object_pt(b) != 0)
3904 {
3905 // tmp_zeta[0] = this->boundary_coordinate_limits(b)[0];
3906 }
3907 else
3908 {
3909 // Check if the initial boundary coordinate is different from
3910 // zero, if that is the case then we need to set it to zero
3911 if (tmp_zeta[0] >= 1.0e-14)
3912 {
3913 tmp_zeta[0] = 0;
3914 }
3915 } // if (this->boundary_geom_object_pt(b)!=0)
3916
3917 // Store the initial zeta value
3918 flat_packed_double_data_initial_seg[2] = tmp_zeta[0];
3919
3920 } // if (my_rank == proc_with_initial_seg)
3921
3922 // All processor receive the info. from the processor that has the
3923 // initial segment
3924 MPI_Bcast(&flat_packed_double_data_initial_seg[0],
3925 ndtotal_data,
3926 MPI_DOUBLE,
3927 proc_with_initial_seg,
3928 comm_pt->mpi_comm());
3929
3930 // ... and all processor put that info. into the appropriate
3931 // storages
3932 for (unsigned i = 0; i < 2; i++)
3933 {
3934 first_coordinate[i] = flat_packed_double_data_initial_seg[i];
3935 }
3936 first_node_zeta_coordinate[0] = flat_packed_double_data_initial_seg[2];
3937
3938 // -----------------------------------------------------------------
3939 // Send three data to all processors, the x[0], x[1] coordinate and
3940 // the zeta coordinate
3941 Vector<double> flat_packed_double_data_final_seg(ndtotal_data);
3942
3943 // If the mesh is distributed then check if this processor has the
3944 // final segment
3945 if (my_rank == proc_with_final_seg)
3946 {
3947 // Get access to the last element on the segment
3948 FiniteElement* last_ele_pt = 0;
3949
3950 // Get the last node of the current segment
3951 Node* last_node_pt = 0;
3952
3953 // Check if the segment is inverted
3954 if (!segment_inverted[final_segment])
3955 {
3956 // Get access to the last element on the segment marked as
3957 // final
3958 last_ele_pt = segment_sorted_ele_pt[final_segment].back();
3959
3960 // Number of nodes
3961 const unsigned nnod = last_ele_pt->nnode();
3962
3963 // Get the last node of the current segment
3964 last_node_pt = last_ele_pt->node_pt(nnod - 1);
3965 if (is_inverted[last_ele_pt])
3966 {
3967 last_node_pt = last_ele_pt->node_pt(0);
3968 }
3969 } // if (!segment_inverted[final_segment])
3970 else
3971 {
3972 // Get access to the first element on the segment marked as
3973 // initial
3974 last_ele_pt = segment_sorted_ele_pt[final_segment].front();
3975
3976 // Number of nodes
3977 const unsigned nnod = last_ele_pt->nnode();
3978
3979 // Get the first node of the current segment
3980 last_node_pt = last_ele_pt->node_pt(0);
3981 if (is_inverted[last_ele_pt])
3982 {
3983 last_node_pt = last_ele_pt->node_pt(nnod - 1);
3984 }
3985 } // if (!segment_inverted[final_segment])
3986
3987 // Get the coordinates for the last node
3988 for (unsigned i = 0; i < 2; i++)
3989 {
3990 flat_packed_double_data_final_seg[i] = last_node_pt->x(i);
3991 }
3992
3993 // Get the zeta coordinates for the last node
3994 Vector<double> tmp_zeta(1);
3995 last_node_pt->get_coordinates_on_boundary(b, tmp_zeta);
3996
3997 // If there is not a geometric object associated to the boundary
3998 // then further process is required
3999 if (this->boundary_geom_object_pt(b) != 0)
4000 {
4001 // Do nothing
4002 } // if (this->boundary_geom_object_pt(b)!=0)
4003 else
4004 {
4005 // Check if the final boundary coordinate is different from
4006 // the boundary arclength, if that is the case then we need
4007 // to set it to the accumulated arclength
4008 if (std::fabs(tmp_zeta[0] - root_accumulated_arclength) >= 1.0e-14)
4009 {
4010 tmp_zeta[0] = root_accumulated_arclength;
4011 }
4012 } // else if (this->boundary_geom_object_pt(b)!=0)
4013
4014 // Store the final zeta value
4015 flat_packed_double_data_final_seg[2] = tmp_zeta[0];
4016
4017 } // if (my_rank == proc_with_final_seg)
4018
4019 // All processor receive the info. from the processor that has the
4020 // final segment
4021 MPI_Bcast(&flat_packed_double_data_final_seg[0],
4022 ndtotal_data,
4023 MPI_DOUBLE,
4024 proc_with_final_seg,
4025 comm_pt->mpi_comm());
4026
4027 // All processor receive the info. from the processor that has the
4028 // final segment
4029 for (unsigned i = 0; i < 2; i++)
4030 {
4031 last_coordinate[i] = flat_packed_double_data_final_seg[i];
4032 }
4033 last_node_zeta_coordinate[0] = flat_packed_double_data_final_seg[2];
4034
4035 // -----------------------------------------------------------------
4036 // Copy the values to the permanent storage
4037 Boundary_initial_coordinate[b] = first_coordinate;
4038 Boundary_final_coordinate[b] = last_coordinate;
4039
4040 Boundary_initial_zeta_coordinate[b] = first_node_zeta_coordinate;
4041 Boundary_final_zeta_coordinate[b] = last_node_zeta_coordinate;
4042
4043 // If we are dealing with an internal boundary then re-assign the
4044 // initial and final zeta values for the segments
4045 if (is_internal_boundary)
4046 {
4047 // Only re-assign zeta values if there are at least one nonhalo
4048 // segment, if all the possible segments are halo then the
4049 // synchronisation method will be in charge of assigning the
4050 // correct boundary coordinates
4051 if (nsegments > 0)
4052 {
4053 // Call the following method to re-construct the segments but
4054 // using only the nonhalo elements, therefore the boundary
4055 // coordinates need to be re-assigned
4056 re_assign_initial_zeta_values_for_internal_boundary(
4057 b, segment_sorted_nonhalo_ele_pt, is_inverted);
4058 }
4059
4060 } // if (is_internal_boundary)
4061
4062 // Now identify the boundary segments
4063 if (nsegments > 0)
4064 {
4065 // Identify the boundary segments in the current mesh
4066 // identify_boundary_segments_and_assign_initial_zeta_values(
4067 // b, all_face_ele_pt, is_internal_boundary, face_to_bulk_element_pt);
4068 identify_boundary_segments_and_assign_initial_zeta_values(b, this);
4069 } // if (nsegments > 0)
4070
4071 // Clean all the created face elements
4072 for (unsigned i = 0; i < n_all_face_ele; i++)
4073 {
4074 delete all_face_ele_pt[i];
4075 all_face_ele_pt[i] = 0;
4076 }
4077 }
4078
4079 //======================================================================
4080 /// Re-assign the boundary segments initial zeta (arclength)
4081 /// for those internal boundaries that were splited during the
4082 /// distribution process. Those boundaries that have one face element
4083 /// at each side of the boundary. Here we create the segments only
4084 /// with the nonhalo elements, therefore the boundary coordinates
4085 /// need to be re-assigned to be passed to the new meshes
4086 //======================================================================
4087 template<class ELEMENT>
4090 const unsigned& b,
4091 Vector<std::list<FiniteElement*>>& old_segment_sorted_ele_pt,
4092 std::map<FiniteElement*, bool>& old_is_inverted)
4093 {
4094 // ------------------------------------------------------------------
4095 // First: Get the face elements associated with the current boundary
4096 // Only include nonhalo face elements
4097 // ------------------------------------------------------------------
4098 // Temporary storage for face elements
4099 Vector<FiniteElement*> face_el_pt;
4100
4101 // Temporary storage for the number of elements adjacent to the
4102 // boundary
4103 unsigned nele = 0;
4104
4105 // Temporary storage for elements adjacent to the boundary that have
4106 // a common edge (related with internal boundaries)
4107 unsigned n_repeated_ele = 0;
4108
4109 const unsigned n_regions = this->nregion();
4110
4111 // Temporary storage for already done nodes
4112 Vector<std::pair<Node*, Node*>> done_nodes_pt;
4113
4114 // If there is more than one region then only use boundary
4115 // coordinates from the bulk side (region 0)
4116 if (n_regions > 1)
4117 {
4118 for (unsigned rr = 0; rr < n_regions; rr++)
4119 {
4120 const unsigned region_id =
4121 static_cast<unsigned>(this->Region_attribute[rr]);
4122
4123 // Loop over all elements on boundaries in region i_r
4124 const unsigned nel_in_region =
4125 this->nboundary_element_in_region(b, region_id);
4126
4127 unsigned nel_repetead_in_region = 0;
4128
4129 // Only bother to do anything else, if there are elements
4130 // associated with the boundary and the current region
4131 if (nel_in_region > 0)
4132 {
4133 bool repeated = false;
4134
4135 // Loop over the bulk elements adjacent to boundary b
4136 for (unsigned e = 0; e < nel_in_region; e++)
4137 {
4138 // Get pointer to the bulk element that is adjacent to
4139 // boundary b
4140 FiniteElement* bulk_elem_pt =
4141 this->boundary_element_in_region_pt(b, region_id, e);
4142
4143 // Remember only work with non halo elements
4144 if (bulk_elem_pt->is_halo())
4145 {
4146 n_repeated_ele++;
4147 continue;
4148 }
4149
4150 // Find the index of the face of element e along boundary b
4151 int face_index =
4152 this->face_index_at_boundary_in_region(b, region_id, e);
4153
4154 // Before adding the new element we need to be sure that the
4155 // edge that this element represent has not been already
4156 // added
4157 FiniteElement* tmp_ele_pt =
4158 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4159
4160 const unsigned n_nodes = tmp_ele_pt->nnode();
4161
4162 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4163 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4164
4165 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4166 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
4167
4168 // Search for repeated nodes
4169 const unsigned repeated_nodes_size = done_nodes_pt.size();
4170 for (unsigned l = 0; l < repeated_nodes_size; l++)
4171 {
4172 if (tmp_pair == done_nodes_pt[l] ||
4173 tmp_pair_inverse == done_nodes_pt[l])
4174 {
4175 nel_repetead_in_region++;
4176 repeated = true;
4177 break;
4178 }
4179 }
4180
4181 // Create new face element
4182 if (!repeated)
4183 {
4184 // Add the pair of nodes (edge) to the node dones
4185 done_nodes_pt.push_back(tmp_pair);
4186 // Add the element to the face elements
4187 face_el_pt.push_back(tmp_ele_pt);
4188 }
4189 else
4190 {
4191 // Clean up
4192 delete tmp_ele_pt;
4193 tmp_ele_pt = 0;
4194 }
4195
4196 // Re-start
4197 repeated = false;
4198
4199 } // for nel
4200
4201 nele += nel_in_region;
4202
4203 n_repeated_ele += nel_repetead_in_region;
4204
4205 } // if (nel_in_region > 0)
4206 } // for (rr < n_regions)
4207 } // if (n_regions > 1)
4208 // Otherwise it's just the normal boundary functions
4209 else
4210 {
4211 // Loop over all elements on boundaries
4212 nele = this->nboundary_element(b);
4213
4214 // Only bother to do anything else, if there are elements
4215 if (nele > 0)
4216 {
4217 // Check for repeated ones
4218 bool repeated = false;
4219
4220 // Loop over the bulk elements adjacent to boundary b
4221 for (unsigned e = 0; e < nele; e++)
4222 {
4223 // Get pointer to the bulk element that is adjacent to
4224 // boundary b
4225 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
4226
4227 // Skip the halo elements, they are not included
4228 if (bulk_elem_pt->is_halo())
4229 {
4230 n_repeated_ele++;
4231 continue;
4232 }
4233
4234 // Find the index of the face of element e along boundary b
4235 int face_index = this->face_index_at_boundary(b, e);
4236
4237 // Before adding the new element we need to be sure that the
4238 // edge that this element represents has not been already
4239 // added (only applies for internal boundaries)
4240 FiniteElement* tmp_ele_pt =
4241 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
4242
4243 const unsigned n_nodes = tmp_ele_pt->nnode();
4244
4245 std::pair<Node*, Node*> tmp_pair = std::make_pair(
4246 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
4247
4248 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
4249 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
4250
4251 // Search for repeated nodes
4252 const unsigned repeated_nodes_size = done_nodes_pt.size();
4253 for (unsigned l = 0; l < repeated_nodes_size; l++)
4254 {
4255 if (tmp_pair == done_nodes_pt[l] ||
4256 tmp_pair_inverse == done_nodes_pt[l])
4257 {
4258 // Increase the number of repeated elements
4259 n_repeated_ele++;
4260 // Mark the element as repeated
4261 repeated = true;
4262 break;
4263 }
4264 }
4265
4266 // Create new face element
4267 if (!repeated)
4268 {
4269 // Add the pair of nodes (edge) to the node dones
4270 done_nodes_pt.push_back(tmp_pair);
4271 // Add the element to the face elements
4272 face_el_pt.push_back(tmp_ele_pt);
4273 }
4274 else
4275 {
4276 // Free the repeated bulk element!!
4277 delete tmp_ele_pt;
4278 tmp_ele_pt = 0;
4279 }
4280
4281 // Re-start
4282 repeated = false;
4283
4284 } // for (e < nel)
4285 } // if (nel > 0)
4286
4287 } // else (n_regions > 1)
4288
4289 // Do not consider the repeated elements
4290 nele -= n_repeated_ele;
4291
4292#ifdef PARANOID
4293 if (nele != face_el_pt.size())
4294 {
4295 std::ostringstream error_message;
4296 error_message
4297 << "The independet counting of face elements (" << nele << ") for "
4298 << "boundary (" << b << ") is different\n"
4299 << "from the real number of face elements in the container ("
4300 << face_el_pt.size() << ")\n";
4301 //<< "Possible memory leak\n"
4302 throw OomphLibError(
4303 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4304 }
4305#endif
4306
4307 // ----------------------------------------------------------------
4308 // Second: Sort the face elements, only consider nonhalo elements
4309 // ----------------------------------------------------------------
4310
4311 // Get the total number of nonhalo face elements
4312 const unsigned nnon_halo_face_elements = face_el_pt.size();
4313
4314 // The vector of list to store the "segments" that compound the
4315 // boundary (segments may appear only in a distributed mesh)
4316 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
4317
4318 // Number of already sorted face elements
4319 unsigned nsorted_face_elements = 0;
4320
4321 // Keep track of who's done
4322 std::map<FiniteElement*, bool> done_el;
4323
4324 // Keep track of which element is inverted
4325 std::map<FiniteElement*, bool> is_inverted;
4326
4327 // Iterate until all possible segments have been created
4328 while (nsorted_face_elements < nnon_halo_face_elements)
4329 {
4330 // The ordered list of face elements (in a distributed mesh a
4331 // collection of contiguous face elements define a segment)
4332 std::list<FiniteElement*> sorted_el_pt;
4333
4334#ifdef PARANOID
4335 // Select an initial element for the segment
4336 bool found_initial_face_element = false;
4337#endif
4338
4339 FiniteElement* ele_face_pt = 0;
4340
4341 unsigned iface = 0;
4342 for (iface = 0; iface < nele; iface++)
4343 {
4344 ele_face_pt = face_el_pt[iface];
4345 // If not done then take it as initial face element
4346 if (!done_el[ele_face_pt])
4347 {
4348#ifdef PARANOID
4349 // Mark as found the root face element
4350 found_initial_face_element = true;
4351#endif
4352 // Increase the number of sorted face elements
4353 nsorted_face_elements++;
4354 // Increase the counter to mark the position of the next
4355 // element number
4356 iface++;
4357 // Add the face element in the list of sorted face elements
4358 sorted_el_pt.push_back(ele_face_pt);
4359 // Mark as done
4360 done_el[ele_face_pt] = true;
4361 break;
4362 } // if (!done_el[ele_face_pt])
4363 } // for (iface < nele)
4364
4365#ifdef PARANOID
4366 if (!found_initial_face_element)
4367 {
4368 std::ostringstream error_message;
4369 error_message
4370 << "Could not find an initial face element for the current segment\n";
4371 throw OomphLibError(
4372 error_message.str(),
4373 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4374 OOMPH_EXCEPTION_LOCATION);
4375 }
4376#endif
4377
4378 // Number of nodes
4379 const unsigned nnod = ele_face_pt->nnode();
4380
4381 // Left and rightmost nodes (the left and right nodes of the
4382 // current face element)
4383 Node* left_node_pt = ele_face_pt->node_pt(0);
4384 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
4385
4386 // Continue iterating if a new face element has been added to the
4387 // list
4388 bool face_element_added = false;
4389
4390 // While a new face element has been added to the set of sorted
4391 // face elements then re-iterate
4392 do
4393 {
4394 // Start from the next face element since we have already added
4395 // the previous one as the initial face element (any previous
4396 // face element had to be added on previous iterations)
4397 for (unsigned iiface = iface; iiface < nele; iiface++)
4398 {
4399 // Re-start flag
4400 face_element_added = false;
4401
4402 // Get the candidate element
4403 ele_face_pt = face_el_pt[iiface];
4404
4405 // Check that the candidate element has not been done and is
4406 // not a halo element
4407 if (!(done_el[ele_face_pt]))
4408 {
4409 // Get the left and right nodes of the current element
4410 Node* local_left_node_pt = ele_face_pt->node_pt(0);
4411 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
4412
4413 // New element fits at the left of segment and is not inverted
4414 if (left_node_pt == local_right_node_pt)
4415 {
4416 left_node_pt = local_left_node_pt;
4417 sorted_el_pt.push_front(ele_face_pt);
4418 is_inverted[ele_face_pt] = false;
4419 face_element_added = true;
4420 }
4421 // New element fits at the left of segment and is inverted
4422 else if (left_node_pt == local_left_node_pt)
4423 {
4424 left_node_pt = local_right_node_pt;
4425 sorted_el_pt.push_front(ele_face_pt);
4426 is_inverted[ele_face_pt] = true;
4427 face_element_added = true;
4428 }
4429 // New element fits on the right of segment and is not inverted
4430 else if (right_node_pt == local_left_node_pt)
4431 {
4432 right_node_pt = local_right_node_pt;
4433 sorted_el_pt.push_back(ele_face_pt);
4434 is_inverted[ele_face_pt] = false;
4435 face_element_added = true;
4436 }
4437 // New element fits on the right of segment and is inverted
4438 else if (right_node_pt == local_right_node_pt)
4439 {
4440 right_node_pt = local_left_node_pt;
4441 sorted_el_pt.push_back(ele_face_pt);
4442 is_inverted[ele_face_pt] = true;
4443 face_element_added = true;
4444 }
4445
4446 if (face_element_added)
4447 {
4448 done_el[ele_face_pt] = true;
4449 nsorted_face_elements++;
4450 break;
4451 } // if (face_element_added)
4452
4453 } // if (!(done_el[ele_face_pt]))
4454
4455 } // for (iiface<nnon_halo_face_element)
4456
4457 } while (face_element_added &&
4458 (nsorted_face_elements < nnon_halo_face_elements));
4459
4460 // Store the created segment in the vector of segments
4461 segment_sorted_ele_pt.push_back(sorted_el_pt);
4462
4463 } // while(nsorted_face_elements < nnon_halo_face_elements);
4464
4465 // --------------------------------------------------------------
4466 // Third: We have the face elements sorted, now assign boundary
4467 // coordinates to the nodes in the segments and compute the
4468 // arclength of the segment.
4469 // --------------------------------------------------------------
4470
4471 // The number of segments in this processor
4472 const unsigned nsegments = segment_sorted_ele_pt.size();
4473
4474#ifdef PARANOID
4475 if (nnon_halo_face_elements > 0 && nsegments == 0)
4476 {
4477 std::ostringstream error_message;
4478 error_message
4479 << "The number of segments is zero, but the number of nonhalo\n"
4480 << "elements is: (" << nnon_halo_face_elements << ")\n";
4481 throw OomphLibError(
4482 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
4483 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
4484#endif
4485
4486 // Vector of sets that stores the nodes of each segment based on a
4487 // lexicographically order starting from the bottom left node of
4488 // each segment
4489 Vector<std::set<Node*>> segment_all_nodes_pt(nsegments);
4490
4491 // Stores the nodes on each segment in the order they appear in the
4492 // face elements
4493 Vector<Vector<Node*>> sorted_segment_all_nodes_pt(nsegments);
4494
4495 // Associate and arclength to each node on each segment of the
4496 // boundary, the nodes and therefore the arclength come in the same
4497 // order as the face elements
4498 Vector<Vector<double>> sorted_segment_node_arclength(nsegments);
4499
4500 // The arclength of each segment in the current processor
4501 Vector<double> segment_arclength(nsegments);
4502
4503 // The number of vertices of each segment
4504 Vector<unsigned> nvertices_per_segment(nsegments);
4505
4506 // The initial zeta for the segment
4507 Vector<double> initial_zeta_segment(nsegments);
4508
4509 // The final zeta for the segment
4510 Vector<double> final_zeta_segment(nsegments);
4511
4512 // Go through all the segments and compute the LOCAL boundary
4513 // coordinates
4514 for (unsigned is = 0; is < nsegments; is++)
4515 {
4516#ifdef PARANOID
4517 if (segment_sorted_ele_pt[is].size() == 0)
4518 {
4519 std::ostringstream error_message;
4520 error_message << "The (" << is << ")-th segment has no elements\n";
4521 throw OomphLibError(
4522 error_message.str(),
4523 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
4524 OOMPH_EXCEPTION_LOCATION);
4525 } // if (segment_sorted_ele_pt[is].size() == 0)
4526#endif
4527
4528 // Get access to the first element on the segment
4529 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
4530
4531 // Number of nodes
4532 const unsigned nnod = first_ele_pt->nnode();
4533
4534 // Get the first node of the current segment
4535 Node* first_node_pt = first_ele_pt->node_pt(0);
4536 if (is_inverted[first_ele_pt])
4537 {
4538 first_node_pt = first_ele_pt->node_pt(nnod - 1);
4539 }
4540
4541 // Coordinates of left node
4542 double x_left = first_node_pt->x(0);
4543 double y_left = first_node_pt->x(1);
4544
4545 // Initialise boundary coordinate (local boundary coordinate for
4546 // boundaries with more than one segment)
4547 Vector<double> zeta(1, 0.0);
4548
4549 // If we have associated a GeomObject then it is not necessary
4550 // to compute the arclength, only read the values from the nodes at
4551 // the edges
4552 if (this->boundary_geom_object_pt(b) != 0)
4553 {
4554 first_node_pt->get_coordinates_on_boundary(b, zeta);
4555 initial_zeta_segment[is] = zeta[0];
4556
4557 // Get access to the last element on the segment
4558 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
4559
4560 // Get the last node of the current segment
4561 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
4562 if (is_inverted[last_ele_pt])
4563 {
4564 last_node_pt = last_ele_pt->node_pt(0);
4565 }
4566
4567 last_node_pt->get_coordinates_on_boundary(b, zeta);
4568 final_zeta_segment[is] = zeta[0];
4569 }
4570
4571 // Sort the nodes in the segment (lexicographically bottom left
4572 // node)
4573 std::set<Node*> local_nodes_pt;
4574 local_nodes_pt.insert(first_node_pt);
4575
4576 // Associate and arclength to the sorted nodes
4577 Vector<double> sorted_node_arclength;
4578 sorted_node_arclength.push_back(0.0);
4579
4580 // Sorts the nodes in the segments according their sorting in the
4581 // face elements
4582 Vector<Node*> sorted_nodes_pt;
4583 sorted_nodes_pt.push_back(first_node_pt);
4584
4585 // Now loop over nodes in order
4586 for (std::list<FiniteElement*>::iterator it =
4587 segment_sorted_ele_pt[is].begin();
4588 it != segment_sorted_ele_pt[is].end();
4589 it++)
4590 {
4591 // Get the face element
4592 FiniteElement* el_pt = *it;
4593
4594 // Start node and increment
4595 unsigned k_nod = 1;
4596 int nod_diff = 1;
4597 if (is_inverted[el_pt])
4598 {
4599 k_nod = nnod - 2;
4600 nod_diff = -1;
4601 }
4602
4603 // Loop over nodes
4604 for (unsigned j = 1; j < nnod; j++)
4605 {
4606 Node* nod_pt = el_pt->node_pt(k_nod);
4607 k_nod += nod_diff;
4608
4609 // Coordinates of right node
4610 double x_right = nod_pt->x(0);
4611 double y_right = nod_pt->x(1);
4612
4613 // Increment boundary coordinate
4614 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
4615 (y_right - y_left) * (y_right - y_left));
4616
4617 // When we have a GeomObject associated to the boundary we already
4618 // know the zeta values for the nodes, there is no need to compute
4619 // the arclength
4620 if (this->boundary_geom_object_pt(b) == 0)
4621 {
4622 // Set boundary coordinate
4623 // nod_pt->set_coordinates_on_boundary(b, zeta);
4624 }
4625
4626 // Increment reference coordinate
4627 x_left = x_right;
4628 y_left = y_right;
4629
4630 // Get lexicographically bottom left node but only
4631 // use vertex nodes as candidates
4632 local_nodes_pt.insert(nod_pt);
4633
4634 // Associate the arclength for the current node
4635 sorted_node_arclength.push_back(zeta[0]);
4636
4637 // Store the node in the sorted nodes storage
4638 sorted_nodes_pt.push_back(nod_pt);
4639
4640 } // for (j < nnod)
4641
4642 } // iterator over the elements in the segment
4643
4644 // Info. to be passed to the other processors
4645 // The initial arclength for the segment that goes after this depends
4646 // on the current segment arclength
4647 segment_arclength[is] = zeta[0];
4648
4649 // Info. to be passed to the other processors
4650 // The initial vertex number for the segment that goes after this
4651 // depends on the current sement vertices number
4652 nvertices_per_segment[is] = local_nodes_pt.size();
4653
4654 // Add the nodes for the corresponding segment in the container
4655 segment_all_nodes_pt[is] = local_nodes_pt;
4656
4657 // Add the arclengths to the nodes in the segment
4658 sorted_segment_node_arclength[is] = sorted_node_arclength;
4659
4660 // Add the sorted nodes to the storage
4661 sorted_segment_all_nodes_pt[is] = sorted_nodes_pt;
4662
4663 // The attaching of the halo elements at both sides of the segments is
4664 // performed only if segments connectivity needs to be computed
4665
4666 } // for (is < nsegments)
4667
4668 // ------------------------------------------------------------------
4669 // Fourth: Now we have the segments sorted, with arclength and with
4670 // LOCAL boundary coordinates assigned to the nodes. Identify the
4671 // nodes on the segments with the input segments and re-assign all
4672 // the info. related with the identification of segments
4673 // ------------------------------------------------------------------
4674
4675 // Get the number of segments for the old sorted segments
4676 const unsigned old_nsegments = old_segment_sorted_ele_pt.size();
4677
4678 // ------------------------------------------------------------------
4679 // Copy the old info. in temporary storages
4680 Vector<unsigned> old_boundary_segment_inverted(old_nsegments);
4681
4682 Vector<Vector<double>> old_boundary_segment_initial_coordinate(
4683 old_nsegments);
4684 Vector<Vector<double>> old_boundary_segment_final_coordinate(old_nsegments);
4685
4686 Vector<double> old_boundary_segment_initial_zeta(old_nsegments);
4687 Vector<double> old_boundary_segment_final_zeta(old_nsegments);
4688
4689 Vector<double> old_boundary_segment_initial_arclength(old_nsegments);
4690 Vector<double> old_boundary_segment_final_arclength(old_nsegments);
4691
4692 // Back-up the information
4693 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4694 {
4695 old_boundary_segment_inverted[old_is] =
4696 boundary_segment_inverted(b)[old_is];
4697
4698 old_boundary_segment_initial_coordinate[old_is].resize(2);
4699 old_boundary_segment_final_coordinate[old_is].resize(2);
4700 for (unsigned i = 0; i < 2; i++)
4701 {
4702 old_boundary_segment_initial_coordinate[old_is][i] =
4703 boundary_segment_initial_coordinate(b)[old_is][i];
4704
4705 old_boundary_segment_final_coordinate[old_is][i] =
4706 boundary_segment_final_coordinate(b)[old_is][i];
4707 }
4708
4709 // Check if the boundary has an associated GeomObject
4710 if (this->boundary_geom_object_pt(b) != 0)
4711 {
4712 old_boundary_segment_initial_zeta[old_is] =
4713 boundary_segment_initial_zeta(b)[old_is];
4714
4715 old_boundary_segment_final_zeta[old_is] =
4716 boundary_segment_final_zeta(b)[old_is];
4717
4718 } // if (this->boundary_geom_object_pt(b)!=0)
4719 else
4720 {
4721 old_boundary_segment_initial_arclength[old_is] =
4722 boundary_segment_initial_arclength(b)[old_is];
4723
4724 old_boundary_segment_final_arclength[old_is] =
4725 boundary_segment_final_arclength(b)[old_is];
4726
4727 } // else if (this->boundary_geom_object_pt(b)!=0)
4728
4729 } // for (old_is < old_nsegments)
4730
4731 // ------------------------------------------------------------------
4732 // Now clear the original storages
4733 Boundary_segment_inverted[b].clear();
4734 Boundary_segment_initial_coordinate[b].clear();
4735 Boundary_segment_final_coordinate[b].clear();
4736
4737 Boundary_segment_initial_zeta[b].clear();
4738 Boundary_segment_final_zeta[b].clear();
4739
4740 Boundary_segment_initial_arclength[b].clear();
4741 Boundary_segment_final_arclength[b].clear();
4742 // ------------------------------------------------------------------
4743 // .. and resize the storages for the new number of segments
4744 Boundary_segment_inverted[b].resize(nsegments);
4745 Boundary_segment_initial_coordinate[b].resize(nsegments);
4746 Boundary_segment_final_coordinate[b].resize(nsegments);
4747
4748 // Check if the boundary has an associated GeomObject
4749 if (this->boundary_geom_object_pt(b) != 0)
4750 {
4751 Boundary_segment_initial_zeta[b].resize(nsegments);
4752 Boundary_segment_final_zeta[b].resize(nsegments);
4753 }
4754 else
4755 {
4756 Boundary_segment_initial_arclength[b].resize(nsegments);
4757 Boundary_segment_final_arclength[b].resize(nsegments);
4758 }
4759 // ------------------------------------------------------------------
4760 // map to know if the new segment has been re-assigned the info.
4761 std::map<unsigned, bool> done_segment;
4762
4763 // Count the number of re-assigned segments with the new values
4764 unsigned re_assigned_segments = 0;
4765
4766 // Go through all the old segments (the input segments)
4767 for (unsigned old_is = 0; old_is < old_nsegments; old_is++)
4768 {
4769 // Get the first and last zeta values for the current segment
4770 const double old_initial_arclength =
4771 old_boundary_segment_initial_arclength[old_is];
4772 const double old_final_arclength =
4773 old_boundary_segment_final_arclength[old_is];
4774 // Get the "is inverted" segment information
4775 const unsigned old_inverted_segment =
4776 old_boundary_segment_inverted[old_is];
4777
4778 // Check if the boundary coordinates in the segment go in
4779 // increasing or decreasing order
4780 bool old_increasing_order = false;
4781 if (old_initial_arclength < old_final_arclength)
4782 {
4783 old_increasing_order = true;
4784 }
4785
4786 // Now get the first and last node of the current segment
4787 // Get the first element
4788 FiniteElement* first_old_seg_ele_pt =
4789 old_segment_sorted_ele_pt[old_is].front();
4790
4791 // Number of nodes
4792 const unsigned nnod = first_old_seg_ele_pt->nnode();
4793
4794 // Get the first node of the current segment
4795 Node* first_old_seg_node_pt = first_old_seg_ele_pt->node_pt(0);
4796 if (old_is_inverted[first_old_seg_ele_pt])
4797 {
4798 first_old_seg_node_pt = first_old_seg_ele_pt->node_pt(nnod - 1);
4799 }
4800
4801 // Get access to the last element on the segment
4802 FiniteElement* last_old_seg_ele_pt =
4803 old_segment_sorted_ele_pt[old_is].back();
4804
4805 // Get the last node of the current segment
4806 Node* last_old_seg_node_pt = last_old_seg_ele_pt->node_pt(nnod - 1);
4807 if (old_is_inverted[last_old_seg_ele_pt])
4808 {
4809 last_old_seg_node_pt = last_old_seg_ele_pt->node_pt(0);
4810 }
4811 // Check if the segment is inverted, if that is the case then
4812 // also invert the nodes
4813 if (old_inverted_segment)
4814 {
4815 Node* temp_node_pt = first_old_seg_node_pt;
4816 first_old_seg_node_pt = last_old_seg_node_pt;
4817 last_old_seg_node_pt = temp_node_pt;
4818 }
4819
4820 // We have the first and last node of the old segment (input
4821 // segment), now identify in which segment, of those with only
4822 // nonhalo face elements, they are
4823 for (unsigned is = 0; is < nsegments; is++)
4824 {
4825 if (!done_segment[is])
4826 {
4827 // Go through the nodes of the current segment and try to find
4828 // the old nodes
4829 bool found_first_old_seg_node = false;
4830 bool found_last_old_seg_node = false;
4831 bool same_order = false;
4832
4833 // Get the first node of the current segment
4834 FiniteElement* first_seg_ele_pt = segment_sorted_ele_pt[is].front();
4835 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
4836 if (is_inverted[first_seg_ele_pt])
4837 {
4838 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
4839 }
4840
4841 // Get the arclength for the first node
4842 const double segment_first_node_zeta =
4843 sorted_segment_node_arclength[is][0];
4844
4845 // Get the node coordinates for the first node
4846 Vector<double> first_node_coord(2);
4847 for (unsigned i = 0; i < 2; i++)
4848 {
4849 first_node_coord[i] = first_seg_node_pt->x(i);
4850 }
4851
4852 // Get the last node of the current segment
4853 FiniteElement* last_seg_ele_pt = segment_sorted_ele_pt[is].back();
4854 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
4855 if (is_inverted[last_seg_ele_pt])
4856 {
4857 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
4858 }
4859
4860 // Get the arclength for the last node
4861 const double segment_final_node_zeta = segment_arclength[is];
4862
4863 // Get the node coordinates for the last node
4864 Vector<double> last_node_coord(2);
4865 for (unsigned i = 0; i < 2; i++)
4866 {
4867 last_node_coord[i] = last_seg_node_pt->x(i);
4868 }
4869
4870 // Temporary storage for the nodes of the current segment
4871 Vector<Node*> segment_node_pt = sorted_segment_all_nodes_pt[is];
4872 // Get the number of nodes in the segment
4873 const unsigned nsegment_node = segment_node_pt.size();
4874 for (unsigned in = 0; in < nsegment_node; in++)
4875 {
4876 Node* current_node_pt = segment_node_pt[in];
4877 if (!found_first_old_seg_node &&
4878 first_old_seg_node_pt == current_node_pt)
4879 {
4880 // Get the arclength assigned to the node on the old
4881 // segment
4882 const double current_node_zeta =
4883 sorted_segment_node_arclength[is][in];
4884
4885 // Now check if the new segment has the same orientation
4886 // as the old one
4887 if (!found_last_old_seg_node) // has the same orientation
4888 {
4889 // Re-assign the first node coordinates
4890 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
4891
4892 // Check if the boundary has an associated GeomObject
4893 if (this->boundary_geom_object_pt(b) != 0)
4894 {
4895 // Assign the zeta values if the current segment has the
4896 // nodes of the old one
4897
4898 // If we are in the same order then pass the values as
4899 // they are
4900 Boundary_segment_initial_zeta[b][is] =
4901 initial_zeta_segment[is];
4902
4903 } // if (this->boundary_geom_object_pt(b)!=0)
4904 else
4905 {
4906 // Get the distance to the first node
4907 const double distance =
4908 std::fabs(current_node_zeta - segment_first_node_zeta);
4909
4910 double new_initial_arclength = old_initial_arclength;
4911
4912 // Now check if the zeta values are in increasing order
4913 if (old_increasing_order)
4914 {
4915 // Substract the distance
4916 new_initial_arclength -= distance;
4917 }
4918 else
4919 {
4920 // Add the distance
4921 new_initial_arclength += distance;
4922 }
4923
4924 // Re-assign the initial arclength for the current segment
4925 Boundary_segment_initial_arclength[b][is] =
4926 new_initial_arclength;
4927
4928 } // else if (this->boundary_geom_object_pt(b)!=0)
4929 } // if (!found_last_old_seg_node)
4930 else // has different orientation
4931 {
4932 // Re-assign the first node coordinates
4933 Boundary_segment_initial_coordinate[b][is] = last_node_coord;
4934
4935 // Check if the boundary has an associated GeomObject
4936 if (this->boundary_geom_object_pt(b) != 0)
4937 {
4938 // Assign the zeta values if the current segment has the
4939 // nodes of the old one
4940
4941 // Not the same order, we need to copy the zeta values
4942 // from the other end, the inverted flag is changed at
4943 // the end. Copy the value from the final end
4944 Boundary_segment_initial_zeta[b][is] = final_zeta_segment[is];
4945
4946 } // if (this->boundary_geom_object_pt(b)!=0)
4947 else
4948 {
4949 // Get the distance to the final node
4950 const double distance =
4951 std::fabs(current_node_zeta - segment_final_node_zeta);
4952
4953 double new_initial_arclength = old_initial_arclength;
4954
4955 // Now check if the zeta values are in increasing order
4956 if (old_increasing_order)
4957 {
4958 // Substract the distance
4959 new_initial_arclength -= distance;
4960 }
4961 else
4962 {
4963 // Add the distance
4964 new_initial_arclength += distance;
4965 }
4966
4967 // Re-assign the initial arclength for the current segment
4968 Boundary_segment_initial_arclength[b][is] =
4969 new_initial_arclength;
4970
4971 } // else if (this->boundary_geom_object_pt(b)!=0)
4972 } // else if (!found_last_old_seg_node)
4973
4974 // Mark as found the first node
4975 found_first_old_seg_node = true;
4976 }
4977 // if (!found_first_old_seg_node &&
4978 // first_old_seg_node_pt == current_node_pt)
4979
4980 // If we found first the first node then the segments have
4981 // the same order
4982 if (found_first_old_seg_node && !found_last_old_seg_node)
4983 {
4984 same_order = true;
4985 }
4986
4987 if (!found_last_old_seg_node &&
4988 last_old_seg_node_pt == current_node_pt)
4989 {
4990 // Get the boundary coordinates assigned to the node on
4991 // the old segment
4992 const double current_node_zeta =
4993 sorted_segment_node_arclength[is][in];
4994
4995 // Now check if the new segment has the same orientation
4996 // as the old one
4997 if (found_first_old_seg_node) // has the same orientation
4998 {
4999 // Re-assign the last node coordinates
5000 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5001
5002 // Check if the boundary has an associated GeomObject
5003 if (this->boundary_geom_object_pt(b) != 0)
5004 {
5005 // Assign the zeta values if the current segment has the
5006 // nodes of the old one
5007
5008 // If we are in the same order then pass the values as
5009 // they are
5010 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5011
5012 } // if (this->boundary_geom_object_pt(b)!=0)
5013 else
5014 {
5015 // Get the distance to the last node
5016 const double distance =
5017 std::fabs(current_node_zeta - segment_final_node_zeta);
5018
5019 double new_final_arclength = old_final_arclength;
5020
5021 // Now check if the zeta values are in increasing order
5022 if (old_increasing_order)
5023 {
5024 // Add the distance
5025 new_final_arclength += distance;
5026 }
5027 else
5028 {
5029 // Substract the distance
5030 new_final_arclength -= distance;
5031 }
5032
5033 // Re-assign the final arclength for the current segment
5034 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5035
5036 } // else if (this->boundary_geom_object_pt(b)!=0)
5037 } // if (found_first_old_seg_node)
5038 else
5039 {
5040 // Re-assign the last node coordinates
5041 Boundary_segment_final_coordinate[b][is] = first_node_coord;
5042
5043 // Check if the boundary has an associated GeomObject
5044 if (this->boundary_geom_object_pt(b) != 0)
5045 {
5046 // Assign the zeta values if the current segment has the
5047 // nodes of the old one
5048
5049 // Not the same order, we need to copy the zeta values
5050 // from the other end, the inverted flag is changed at
5051 // the end. Copy the value from the initial end
5052 Boundary_segment_final_zeta[b][is] = initial_zeta_segment[is];
5053
5054 } // if (this->boundary_geom_object_pt(b)!=0)
5055 else
5056 {
5057 // Get the distance to the last node
5058 const double distance =
5059 std::fabs(current_node_zeta - segment_first_node_zeta);
5060
5061 double new_final_arclength = old_final_arclength;
5062
5063 // Now check if the zeta values are in increasing order
5064 if (old_increasing_order)
5065 {
5066 // Add the distance
5067 new_final_arclength += distance;
5068 }
5069 else
5070 {
5071 // Substract the distance
5072 new_final_arclength -= distance;
5073 }
5074
5075 // Re-assign the final arclength for the current segment
5076 Boundary_segment_final_arclength[b][is] = new_final_arclength;
5077
5078 } // else if (this->boundary_geom_object_pt(b)!=0)
5079 } // if (found_first_old_seg_node)
5080
5081 // Mark as found the last node
5082 found_last_old_seg_node = true;
5083
5084 } // if (!found_last_old_seg_node &&
5085 // last_old_seg_node_pt == current_node_pt)
5086
5087 // If we found the last node first then the segments have
5088 // not the same order
5089 if (!found_first_old_seg_node && found_last_old_seg_node)
5090 {
5091 same_order = false;
5092 }
5093
5094 if (found_first_old_seg_node && found_last_old_seg_node)
5095 {
5096 // Check if necessary to change the information that
5097 // states if a segment is inverted or not
5098 if (same_order)
5099 {
5100 Boundary_segment_inverted[b][is] = old_inverted_segment;
5101 }
5102 else
5103 {
5104 Boundary_segment_inverted[b][is] = !old_inverted_segment;
5105 }
5106
5107 // Mark the segment as done
5108 done_segment[is] = true;
5109
5110 // Increase the number of re-assigned segments
5111 re_assigned_segments++;
5112
5113 // Break the for that look for the nodes in the segments
5114 break;
5115 }
5116
5117 } // for (in < nsegment_node)
5118
5119#ifdef PARANOID
5120 if ((found_first_old_seg_node && !found_last_old_seg_node) ||
5121 (!found_first_old_seg_node && found_last_old_seg_node))
5122 {
5123 std::stringstream error_message;
5124 error_message
5125 << "Working with boundary (" << b << ").\nOnly the first node or "
5126 << "the last node of the old segment (" << old_is << ") was\n"
5127 << "found. Both, first and last node should have been found in "
5128 << "the same segment!!!.\n"
5129 << "Found first seg node:" << found_first_old_seg_node << "\n"
5130 << "Found last seg node:" << found_last_old_seg_node << "\n\n";
5131 throw OomphLibError(error_message.str(),
5132 "TriangleMesh::re_assign_initial_zeta_values_"
5133 "for_internal_boundary()",
5134 OOMPH_EXCEPTION_LOCATION);
5135 }
5136#endif
5137
5138 } // if (!done_segment[is])
5139 } // for (is < nsegments)
5140 } // for (old_is < old_nsegments)
5141
5142 // For those segments not identified set dummy values, the boundary
5143 // coordinates should be corrected at the synchronisation stage
5144
5145 // loop over the new segments and check if there not identified
5146 // segments
5147 for (unsigned is = 0; is < nsegments; is++)
5148 {
5149 // Was the segment identified
5150 if (!done_segment[is])
5151 {
5152 // Get the first node of the current segment
5153 FiniteElement* first_seg_ele_pt = segment_sorted_ele_pt[is].front();
5154 // Number of nodes
5155 const unsigned nnod = first_seg_ele_pt->nnode();
5156
5157 Node* first_seg_node_pt = first_seg_ele_pt->node_pt(0);
5158 if (is_inverted[first_seg_ele_pt])
5159 {
5160 first_seg_node_pt = first_seg_ele_pt->node_pt(nnod - 1);
5161 }
5162
5163 // Get the arclength for the first node
5164 const double segment_first_node_zeta =
5165 sorted_segment_node_arclength[is][0];
5166
5167 // Get the node coordinates for the first node
5168 Vector<double> first_node_coord(2);
5169 for (unsigned i = 0; i < 2; i++)
5170 {
5171 first_node_coord[i] = first_seg_node_pt->x(i);
5172 }
5173
5174 // Get the last node of the current segment
5175 FiniteElement* last_seg_ele_pt = segment_sorted_ele_pt[is].back();
5176 Node* last_seg_node_pt = last_seg_ele_pt->node_pt(nnod - 1);
5177 if (is_inverted[last_seg_ele_pt])
5178 {
5179 last_seg_node_pt = last_seg_ele_pt->node_pt(0);
5180 }
5181
5182 // Get the arclength for the last node
5183 const double segment_final_node_zeta = segment_arclength[is];
5184
5185 // Get the node coordinates for the last node
5186 Vector<double> last_node_coord(2);
5187 for (unsigned i = 0; i < 2; i++)
5188 {
5189 last_node_coord[i] = last_seg_node_pt->x(i);
5190 }
5191
5192 // Re-assign the initial node coordinates
5193 Boundary_segment_initial_coordinate[b][is] = first_node_coord;
5194
5195 // Check if the boundary has an associated GeomObject
5196 if (this->boundary_geom_object_pt(b) != 0)
5197 {
5198 // Assign the zeta values if the current segment has the
5199 // nodes of the old one
5200
5201 // If we are in the same order then pass the values as
5202 // they are
5203 Boundary_segment_initial_zeta[b][is] = initial_zeta_segment[is];
5204
5205 } // if (this->boundary_geom_object_pt(b)!=0)
5206 else
5207 {
5208 // Re-assign the initial arclength for the current segment
5209 Boundary_segment_initial_arclength[b][is] = segment_first_node_zeta;
5210
5211 } // else if (this->boundary_geom_object_pt(b)!=0)
5212
5213 // Re-assign the initial node coordinates
5214 Boundary_segment_final_coordinate[b][is] = last_node_coord;
5215
5216 // Check if the boundary has an associated GeomObject
5217 if (this->boundary_geom_object_pt(b) != 0)
5218 {
5219 // Assign the zeta values if the current segment has the
5220 // nodes of the old one
5221
5222 // If we are in the same order then pass the values as
5223 // they are
5224 Boundary_segment_final_zeta[b][is] = final_zeta_segment[is];
5225
5226 } // if (this->boundary_geom_object_pt(b)!=0)
5227 else
5228 {
5229 // Re-assign the final arclength for the current segment
5230 Boundary_segment_final_arclength[b][is] = segment_final_node_zeta;
5231
5232 } // else if (this->boundary_geom_object_pt(b)!=0)
5233
5234 Boundary_segment_inverted[b][is] = 0;
5235
5236 // Mark the segment as done
5237 done_segment[is] = true;
5238
5239 // Increase the number of re-assigned segments
5240 re_assigned_segments++;
5241
5242 } // if (!done_segment[is])
5243
5244 } // for (is < nsegments)
5245
5246#ifdef PARANOID
5247 // Compare the number of new segments identified with the old segments
5248 if (re_assigned_segments != nsegments)
5249 {
5250 std::stringstream error_message;
5251 error_message << "Working with boundary (" << b
5252 << ").\nThe number of re-assigned "
5253 << "segments (" << re_assigned_segments
5254 << ") is different from the number\nof segments ("
5255 << nsegments << ")\n\n";
5256 throw OomphLibError(
5257 error_message.str(),
5258 "TriangleMesh::re_assign_initial_zeta_values_for_internal_boundary()",
5259 OOMPH_EXCEPTION_LOCATION);
5260 } // if (re_assigned_segments != nsegments)
5261#endif
5262
5263 // Clean all the created face elements
5264 for (unsigned i = 0; i < nele; i++)
5265 {
5266 delete face_el_pt[i];
5267 face_el_pt[i] = 0;
5268 }
5269 }
5270
5271 ///=====================================================================
5272 /// Select face elements from a given boundary. In case the we are
5273 /// dealing with an internal boundary we use a set of criterias to
5274 /// decide which of the two face elements should be used on represent
5275 /// the internal boundary. We return the face elements, halo or
5276 /// haloed on this processor that form the boundary. The caller method
5277 /// should be in charge of selecting nonhalo elements and deleting the face
5278 /// elements created by this method
5279 /// =====================================================================
5280 template<class ELEMENT>
5282 Vector<FiniteElement*>& face_ele_pt,
5283 const unsigned& b,
5284 bool& is_internal_boundary,
5285 std::map<FiniteElement*, FiniteElement*>& face_to_bulk_element_pt)
5286 {
5287 // Get the communicator of the mesh
5288 OomphCommunicator* comm_pt = this->communicator_pt();
5289
5290 const unsigned my_rank = comm_pt->my_rank();
5291
5292 // ------------------------------------------------------------------
5293 // 1) Get the face elements associated with the current boundary
5294 // ------------------------------------------------------------------
5295
5296 // Temporary storage for face elements (do not take care of
5297 // repeated face elements)
5298 Vector<FiniteElement*> tmp_face_ele_pt;
5299
5300 const unsigned nregions = this->nregion();
5301
5302 // If there is more than one region then only use boundary
5303 // coordinates from the bulk side (region 0)
5304 if (nregions > 1)
5305 {
5306 for (unsigned ir = 0; ir < nregions; ir++)
5307 {
5308 const unsigned region_id =
5309 static_cast<unsigned>(this->Region_attribute[ir]);
5310
5311 // Loop over all elements on boundaries in region -ir-
5312 const unsigned nele_in_region =
5313 this->nboundary_element_in_region(b, region_id);
5314
5315 // Only bother to do anything else, if there are elements
5316 // associated with the boundary and the current region
5317 if (nele_in_region > 0)
5318 {
5319 // Loop over the bulk elements adjacent to boundary b
5320 for (unsigned e = 0; e < nele_in_region; e++)
5321 {
5322 // Get pointer to the bulk element that is adjacent
5323 // to boundary b
5324 FiniteElement* bulk_ele_pt =
5325 this->boundary_element_in_region_pt(b, region_id, e);
5326
5327 // Get the index of the face of element e along
5328 // boundary b
5329 int face_index =
5330 this->face_index_at_boundary_in_region(b, region_id, e);
5331
5332 // Create the face element
5333 FiniteElement* tmp_face_el_pt =
5334 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5335
5336 // Associated the face element with the bulk
5337 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5338
5339 // ... and add it to the tmp storage for all the
5340 // face elements, do not take care for repeated
5341 // ones (at the moment)
5342 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5343
5344 } // for (e < nele_in_region)
5345
5346 } // if (nele_in_region > 0)
5347
5348 } // for (ir < n_regions)
5349
5350 } // if (n_regions > 1)
5351
5352 // Otherwise it's just the normal boundary functions
5353 else
5354 {
5355 // Loop over all elements on boundaries
5356 const unsigned nbound_ele = this->nboundary_element(b);
5357
5358 // Only bother to do anything else, if there are elements
5359 if (nbound_ele > 0)
5360 {
5361 // Loop over the bulk elements adjacent to boundary b
5362 for (unsigned e = 0; e < nbound_ele; e++)
5363 {
5364 // Get pointer to the bulk element that is adjacent to
5365 // boundary b
5366 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5367
5368 // Get the index of the face of element e along
5369 // boundary b
5370 int face_index = this->face_index_at_boundary(b, e);
5371
5372 // Create the face element
5373 FiniteElement* tmp_face_el_pt =
5374 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5375
5376 // Associated the face element with the bulk
5377 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5378
5379 // ... and add it to the tmp storage for all the face
5380 // elements, do not care for repeated ones (at the
5381 // moment)
5382 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5383
5384 } // (e < nbound_ele)
5385
5386 } // (nbound_ele > 0)
5387
5388 } // else (n_regions > 1)
5389
5390 // map to know which face element has been already done
5391 std::map<FiniteElement*, bool> done_face;
5392
5393 // Set the flag to indicate if we are working with an internal
5394 // boundary
5395 is_internal_boundary = false;
5396
5397 // Free the memory of the elements in this container (only used
5398 // when working with internal boundaries)
5399 Vector<FiniteElement*> free_memory_face_ele_pt;
5400
5401 // Get the number of face elements in the boundary (including
5402 // repeated)
5403 const unsigned n_tmp_face_ele = tmp_face_ele_pt.size();
5404 for (unsigned ie = 0; ie < n_tmp_face_ele; ie++)
5405 {
5406 // Get the possible main element
5407 FiniteElement* main_face_ele_pt = tmp_face_ele_pt[ie];
5408 if (!done_face[main_face_ele_pt])
5409 {
5410 // Mark the face element as done
5411 done_face[main_face_ele_pt] = true;
5412 // Get the number of nodes for the face element
5413 const unsigned nnodes = main_face_ele_pt->nnode();
5414 // Get the first and last node of the main face element
5415 Node* main_first_node_pt = main_face_ele_pt->node_pt(0);
5416 Node* main_last_node_pt = main_face_ele_pt->node_pt(nnodes - 1);
5417 // Look for the other side face element (we can start from
5418 // the next one, all previous face elements have been
5419 // already identified with its other side face)
5420 for (unsigned iie = ie + 1; iie < n_tmp_face_ele; iie++)
5421 {
5422 // Get the possible dependant element
5423 FiniteElement* dependant_face_ele_pt = tmp_face_ele_pt[iie];
5424 if (!done_face[dependant_face_ele_pt])
5425 {
5426 // Get the first and last node of the dependant
5427 // face element
5428 Node* dependant_first_node_pt = dependant_face_ele_pt->node_pt(0);
5429 Node* dependant_last_node_pt =
5430 dependant_face_ele_pt->node_pt(nnodes - 1);
5431 // Check if the nodes at the ends of both face
5432 // elements match (also check the reversed case)
5433 if (((dependant_first_node_pt == main_first_node_pt) &&
5434 (dependant_last_node_pt == main_last_node_pt)) ||
5435 ((dependant_first_node_pt == main_last_node_pt) &&
5436 (dependant_last_node_pt == main_first_node_pt)))
5437 {
5438 // Set the flag to indicate we are working with an
5439 // internal boundary
5440 is_internal_boundary = true;
5441 // Mark the face element as done
5442 done_face[dependant_face_ele_pt] = true;
5443
5444 // Now choose which face element will be used
5445 // as the main element. We get the processor in
5446 // charge of the element and choose the one
5447 // with the highest processor in charge or the
5448 // bottom-left bulk element in case the both
5449 // faces are on the same processor
5450
5451 // Get the bulk element for each face element
5452 // (the main and the dependant face element)
5453 FiniteElement* main_bulk_ele_pt =
5454 face_to_bulk_element_pt[main_face_ele_pt];
5455 FiniteElement* dependant_bulk_ele_pt =
5456 face_to_bulk_element_pt[dependant_face_ele_pt];
5457
5458 // Get the processor in charge for each bulk
5459 // element
5460 int processor_in_charge_main_bulk_ele =
5461 main_bulk_ele_pt->non_halo_proc_ID();
5462 int processor_in_charge_dependant_bulk_ele =
5463 dependant_bulk_ele_pt->non_halo_proc_ID();
5464
5465 // If the processor in charge is negative the
5466 // element is not halo, therefore the processor
5467 // in charge is the current one
5468 if (processor_in_charge_main_bulk_ele < 0)
5469 {
5470 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5471 }
5472 if (processor_in_charge_dependant_bulk_ele < 0)
5473 {
5474 processor_in_charge_dependant_bulk_ele =
5475 static_cast<int>(my_rank);
5476 }
5477
5478 // Flag to know if add the main or dependant
5479 // face element
5480 bool add_main_face_element = true;
5481 if (processor_in_charge_dependant_bulk_ele >
5482 processor_in_charge_main_bulk_ele)
5483 {
5484 // Include the dependant element
5485 add_main_face_element = false;
5486 }
5487 else if (processor_in_charge_main_bulk_ele ==
5488 processor_in_charge_dependant_bulk_ele)
5489 {
5490 // When the processor in charge for both
5491 // elements is the same then use the
5492 // bottom-left criteria on the bulk
5493 // elements to choose the main face element
5494 Vector<double> main_ele_coordinates(2);
5495 Vector<double> dependant_ele_coordinates(2);
5496 // Get the number of nodes on the bulk
5497 // elements
5498 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5499 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5500 {
5501 for (unsigned idim = 0; idim < 2; idim++)
5502 {
5503 main_ele_coordinates[idim] +=
5504 main_bulk_ele_pt->node_pt(inode)->x(idim);
5505 dependant_ele_coordinates[idim] +=
5506 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
5507 } // (idim < 2)
5508
5509 } // (inode < n_bulk_nodes)
5510
5511 // Get the average of the nodes coordinates
5512 for (unsigned idim = 0; idim < 2; idim++)
5513 {
5514 main_ele_coordinates[idim] /= (double)n_bulk_nodes;
5515 dependant_ele_coordinates[idim] /= (double)n_bulk_nodes;
5516 }
5517
5518 // Once we know the average coordinates for
5519 // each element then we choose the one with
5520 // the bottom-left averaged coordinates
5521 if (dependant_ele_coordinates[1] < main_ele_coordinates[1])
5522 {
5523 add_main_face_element = false;
5524 }
5525 else if (dependant_ele_coordinates[1] ==
5526 main_ele_coordinates[1])
5527 {
5528 // The left-most element
5529 if (dependant_ele_coordinates[0] < main_ele_coordinates[0])
5530 {
5531 add_main_face_element = false;
5532 }
5533 }
5534 } // else -- The processor in charge is the
5535 // same for both elements
5536
5537 if (add_main_face_element)
5538 {
5539 // Add the main face element to the storage
5540 // so we get the halo and haloed nodes from
5541 // it
5542 face_ele_pt.push_back(main_face_ele_pt);
5543 // Mark the dependat face element to free
5544 // its memory
5545 free_memory_face_ele_pt.push_back(dependant_face_ele_pt);
5546 }
5547 else
5548 {
5549 // Add the dependant face element to the
5550 // storage so we get the halo and haloed
5551 // nodes from it
5552 face_ele_pt.push_back(dependant_face_ele_pt);
5553 // Mark the main face element to free its
5554 // memory
5555 free_memory_face_ele_pt.push_back(main_face_ele_pt);
5556 }
5557
5558 // Break the for to look for the next face
5559 // element
5560 break;
5561
5562 } // if -- matching of nodes from main ele and
5563 // dependant ele
5564
5565 } // if (!done_face[dependant_face_ele_pt])
5566
5567 } // for (iie < n_tmp_face_ele)
5568
5569 } // if (!done_face[main_face_ele_pt])
5570
5571 } // for (ie < n_tmp_face_ele)
5572
5573 // Are there any face element to free its memory
5574 const unsigned n_free_face_ele = free_memory_face_ele_pt.size();
5575 if (n_free_face_ele == 0)
5576 {
5577 // If there is not face elements to free memory that means that
5578 // we are not working with an internal boundary, therefore copy
5579 // all the element from the tmp face elements into the face
5580 // elements container
5581
5582 // Resize the container
5583 face_ele_pt.resize(n_tmp_face_ele);
5584 // loop over the elements and copy them
5585 for (unsigned i = 0; i < n_tmp_face_ele; i++)
5586 {
5587 face_ele_pt[i] = tmp_face_ele_pt[i];
5588 } // for (i < n_tmp_face_ele)
5589
5590 } // if (n_free_face_ele == 0)
5591 else
5592 {
5593 // ... otherwise free the memory of the indicated elements
5594 // loop over the elements to free its memory
5595 for (unsigned i = 0; i < n_free_face_ele; i++)
5596 {
5597 delete free_memory_face_ele_pt[i];
5598 free_memory_face_ele_pt[i] = 0;
5599 } // for (i < n_free_face_ele)
5600 }
5601 }
5602
5603 ///========================================================================
5604 /// In charge of sinchronize the boundary coordinates for internal
5605 /// boundaries that were split as part of the distribution
5606 /// process. Called after setup_boundary_coordinates() for the
5607 /// original mesh only
5608 ///========================================================================
5609 template<class ELEMENT>
5611 const unsigned& b)
5612 {
5613 // ------------------------------------------------------------------
5614 // First: Get the face elements associated with the current boundary
5615 // ------------------------------------------------------------------
5616
5617 // Get the communicator of the mesh
5618 OomphCommunicator* comm_pt = this->communicator_pt();
5619
5620 const unsigned nproc = comm_pt->nproc();
5621 const unsigned my_rank = comm_pt->my_rank();
5622
5623 // Temporary storage for face elements (do not take care of repeated
5624 // face elements)
5625 Vector<FiniteElement*> tmp_face_ele_pt;
5626
5627 const unsigned nregions = this->nregion();
5628
5629 // map to associate the face element to the bulk element, necessary
5630 // to get the processor in charge for the halo elements
5631 std::map<FiniteElement*, FiniteElement*> face_to_bulk_element_pt;
5632
5633 // If there is more than one region then only use boundary
5634 // coordinates from the bulk side (region 0)
5635 if (nregions > 1)
5636 {
5637 for (unsigned ir = 0; ir < nregions; ir++)
5638 {
5639 const unsigned region_id =
5640 static_cast<unsigned>(this->Region_attribute[ir]);
5641
5642 // Loop over all elements on boundaries in region -ir-
5643 const unsigned nele_in_region =
5644 this->nboundary_element_in_region(b, region_id);
5645
5646 // Only bother to do anything else, if there are elements
5647 // associated with the boundary and the current region
5648 if (nele_in_region > 0)
5649 {
5650 // Loop over the bulk elements adjacent to boundary b
5651 for (unsigned e = 0; e < nele_in_region; e++)
5652 {
5653 // Get pointer to the bulk element that is adjacent to boundary b
5654 FiniteElement* bulk_ele_pt =
5655 this->boundary_element_in_region_pt(b, region_id, e);
5656
5657 // Get the index of the face of element e along boundary b
5658 int face_index =
5659 this->face_index_at_boundary_in_region(b, region_id, e);
5660
5661 // Create the face element
5662 FiniteElement* tmp_face_el_pt =
5663 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5664
5665 // ... and add it to the tmp storage for all the face
5666 // elements, do not take care for repeated ones (at the
5667 // moment)
5668 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5669 // Create the map to know if the element is halo
5670 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5671
5672 } // for (e < nele_in_region)
5673
5674 } // if (nele_in_region > 0)
5675
5676 } // for (ir < n_regions)
5677
5678 } // if (n_regions > 1)
5679
5680 // Otherwise it's just the normal boundary functions
5681 else
5682 {
5683 // Loop over all elements on boundaries
5684 const unsigned nbound_ele = this->nboundary_element(b);
5685
5686 // Only bother to do anything else, if there are elements
5687 if (nbound_ele > 0)
5688 {
5689 // Loop over the bulk elements adjacent to boundary b
5690 for (unsigned e = 0; e < nbound_ele; e++)
5691 {
5692 // Get pointer to the bulk element that is adjacent to boundary b
5693 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
5694
5695 // Get the index of the face of element e along boundary b
5696 int face_index = this->face_index_at_boundary(b, e);
5697
5698 // Create the face element
5699 FiniteElement* tmp_face_el_pt =
5700 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
5701
5702 // ... and add it to the tmp storage for all the face
5703 // elements, do not care for repeated ones (at the moment)
5704 tmp_face_ele_pt.push_back(tmp_face_el_pt);
5705 // Create the map to know if the element is halo
5706 face_to_bulk_element_pt[tmp_face_el_pt] = bulk_ele_pt;
5707
5708 } // (e < nbound_ele)
5709
5710 } // (nbound_ele > 0)
5711
5712 } // else (n_regions > 1)
5713
5714 // Temporary storage for one side face elements. In case we are
5715 // working with an internal boundary here we store only one of the
5716 // face elements that are at each side of the boundary
5717 Vector<FiniteElement*> face_ele_pt;
5718
5719 // map to know which face element has been already done
5720 std::map<FiniteElement*, bool> done_face;
5721
5722 // Flag to indicate if we are working with an internal boundary
5723 bool is_internal_boundary = false;
5724
5725#ifdef PARANOID
5726 // Flag to indicate if we are working with an internal boundary (paranoid)
5727 bool is_internal_boundary_paranoid = false;
5728
5729 // Count the number of other side face elements found in case we are
5730 // working with an internal boundary
5731 unsigned nfound_face_elements = 0;
5732#endif
5733
5734 // Get the number of face elements in the boundary
5735 const unsigned nbound_ele = tmp_face_ele_pt.size();
5736 for (unsigned ie = 0; ie < nbound_ele; ie++)
5737 {
5738 // Get the possible main element
5739 FiniteElement* main_face_ele_pt = tmp_face_ele_pt[ie];
5740 if (!done_face[main_face_ele_pt])
5741 {
5742 // Mark the face element as done
5743 done_face[main_face_ele_pt] = true;
5744 // Get the number of nodes for the face element
5745 const unsigned nnodes = main_face_ele_pt->nnode();
5746 // Get the first and last node of the main face element
5747 Node* main_first_node_pt = main_face_ele_pt->node_pt(0);
5748 Node* main_last_node_pt = main_face_ele_pt->node_pt(nnodes - 1);
5749 // Look for the other side face element
5750 for (unsigned iie = ie + 1; iie < nbound_ele; iie++)
5751 {
5752 // Get the possible dependant element
5753 FiniteElement* dependant_face_ele_pt = tmp_face_ele_pt[iie];
5754 if (!done_face[dependant_face_ele_pt])
5755 {
5756 // Get the first and last node of the dependant face element
5757 Node* dependant_first_node_pt = dependant_face_ele_pt->node_pt(0);
5758 Node* dependant_last_node_pt =
5759 dependant_face_ele_pt->node_pt(nnodes - 1);
5760 // Check if the nodes at the ends of both face elements
5761 // match (also check the reversed case)
5762 if (((dependant_first_node_pt == main_first_node_pt) &&
5763 (dependant_last_node_pt == main_last_node_pt)) ||
5764 ((dependant_first_node_pt == main_last_node_pt) &&
5765 (dependant_last_node_pt == main_first_node_pt)))
5766 {
5767#ifdef PARANOID
5768 // Increase the number of found face elements
5769 nfound_face_elements += 2;
5770#endif
5771 // Set the flag to indicate we are working with an
5772 // internal boundary
5773 is_internal_boundary = true;
5774 // Mark the face element as done
5775 done_face[dependant_face_ele_pt] = true;
5776
5777 // Now choose which face element will be used as the main
5778 // element. Use the same criteria as the compute segments
5779 // connectivity method (highest processor in charge or
5780 // bottom-left bulk element)
5781
5782 // Get the bulk element for each face element (the main
5783 // and the dependant face element)
5784 FiniteElement* main_bulk_ele_pt =
5785 face_to_bulk_element_pt[main_face_ele_pt];
5786 FiniteElement* dependant_bulk_ele_pt =
5787 face_to_bulk_element_pt[dependant_face_ele_pt];
5788
5789 // Get the processor in charge for each bulk element
5790 int processor_in_charge_main_bulk_ele =
5791 main_bulk_ele_pt->non_halo_proc_ID();
5792 int processor_in_charge_dependant_bulk_ele =
5793 dependant_bulk_ele_pt->non_halo_proc_ID();
5794
5795 // If the processor in charge is negative the element is
5796 // not halo, therefore the processor in charge is the
5797 // current one
5798 if (processor_in_charge_main_bulk_ele < 0)
5799 {
5800 processor_in_charge_main_bulk_ele = static_cast<int>(my_rank);
5801 }
5802 if (processor_in_charge_dependant_bulk_ele < 0)
5803 {
5804 processor_in_charge_dependant_bulk_ele =
5805 static_cast<int>(my_rank);
5806 }
5807
5808 // Flag to know if add the main or dependant face element
5809 bool add_main_face_element = true;
5810 if (processor_in_charge_dependant_bulk_ele >
5811 processor_in_charge_main_bulk_ele)
5812 {
5813 // Include the dependant element
5814 add_main_face_element = false;
5815 }
5816 else if (processor_in_charge_main_bulk_ele ==
5817 processor_in_charge_dependant_bulk_ele)
5818 {
5819 // When the processor in charge for both elements is the same
5820 // then use the bottom-left criteria on the bulk elements to
5821 // choose the main face element
5822 Vector<double> main_ele_coordinates(2);
5823 Vector<double> dependant_ele_coordinates(2);
5824 // Get the number of nodes on the bulk elements
5825 const unsigned n_bulk_nodes = main_bulk_ele_pt->nnode();
5826 for (unsigned inode = 0; inode < n_bulk_nodes; inode++)
5827 {
5828 for (unsigned idim = 0; idim < 2; idim++)
5829 {
5830 main_ele_coordinates[idim] +=
5831 main_bulk_ele_pt->node_pt(inode)->x(idim);
5832 dependant_ele_coordinates[idim] +=
5833 dependant_bulk_ele_pt->node_pt(inode)->x(idim);
5834 } // (idim < 2)
5835 } // (inode < n_bulk_nodes)
5836
5837 // Get the average of the nodes coordinates
5838 for (unsigned idim = 0; idim < 2; idim++)
5839 {
5840 main_ele_coordinates[idim] /= (double)n_bulk_nodes;
5841 dependant_ele_coordinates[idim] /= (double)n_bulk_nodes;
5842 }
5843
5844 // Once we know the average coordinates for each element
5845 // then we choose the one with the bottom-left averaged
5846 // coordinates
5847 if (dependant_ele_coordinates[1] < main_ele_coordinates[1])
5848 {
5849 add_main_face_element = false;
5850 }
5851 else if (dependant_ele_coordinates[1] ==
5852 main_ele_coordinates[1])
5853 {
5854 // The left-most element
5855 if (dependant_ele_coordinates[0] < main_ele_coordinates[0])
5856 {
5857 add_main_face_element = false;
5858 }
5859 }
5860 } // else -- The processor in charge is the same for both
5861 // elements
5862
5863 if (add_main_face_element)
5864 {
5865 // Add the main face element to the storage so we get
5866 // the halo and haloed nodes from these face element
5867 face_ele_pt.push_back(main_face_ele_pt);
5868 }
5869 else
5870 {
5871 // Add the main face element to the storage so we get
5872 // the halo and haloed nodes from these face element
5873 face_ele_pt.push_back(dependant_face_ele_pt);
5874 }
5875
5876 // Break the for to look for the next face element
5877 break;
5878
5879 } // if -- matching of nodes from main ele and dependant ele
5880 } // if (!done_face[dependant_face_ele_pt])
5881 } // for (iie < nbound_ele)
5882 } // if (!done_face[main_face_ele_pt])
5883 } // for (ie < nbound_ele)
5884
5885 // Get the number of face elements
5886 const unsigned nface_ele = face_ele_pt.size();
5887
5888#ifdef PARANOID
5889 // Check if we are working with an internal open curve. First check
5890 // if there are elements, in a distributed approach they may be no
5891 // elements associated to the boundary
5892 if (nbound_ele > 0 && nfound_face_elements == nbound_ele)
5893 {
5894 is_internal_boundary_paranoid = true;
5895 }
5896
5897 if (nbound_ele > 0 && is_internal_boundary_paranoid &&
5898 nbound_ele != nface_ele * 2)
5899 {
5900 std::ostringstream error_message;
5901 error_message
5902 << "The info. to perform the synchronisation of the boundary "
5903 << "coordinates was not completely established\n"
5904 << "In this case it was the number of non repeated boundary elements\n"
5905 << "Number of boundary elements: (" << nbound_ele << ")\n"
5906 << "Number of nonrepeated boundary elements: (" << nface_ele << ")\n";
5907 throw OomphLibError(error_message.str(),
5908 "TriangleMesh::synchronize_boundary_coordinates()",
5909 OOMPH_EXCEPTION_LOCATION);
5910 }
5911#endif
5912
5913 // ----------------------------------------------------------------
5914 // Second: Identify the halo face elements
5915 // ----------------------------------------------------------------
5916
5917 // A flag vector to mark those face elements that are considered as
5918 // halo in the current processor
5919 std::vector<bool> is_halo_face_element(nface_ele, false);
5920
5921 // Count the total number of non halo face elements
5922 unsigned nnon_halo_face_elements = 0;
5923
5924 for (unsigned ie = 0; ie < nface_ele; ie++)
5925 {
5926 FiniteElement* face_el_pt = face_ele_pt[ie];
5927 // Get the bulk element
5928 FiniteElement* tmp_bulk_ele_pt = face_to_bulk_element_pt[face_el_pt];
5929 // Check if the bulk element is halo
5930 if (!tmp_bulk_ele_pt->is_halo())
5931 {
5932 is_halo_face_element[ie] = false;
5933 nnon_halo_face_elements++;
5934 }
5935 else
5936 {
5937 // Mark the face element as halo
5938 is_halo_face_element[ie] = true;
5939 }
5940 } // for (ie < nface_ele)
5941
5942 // -----------------------------------------------------------------
5943 // Third: Go through the face elements and get the nodes from the
5944 // elements. The boundary coordinate from each node is sent to its
5945 // processor in charge, then that processor will be responsible to
5946 // send the bound coordinate to all the processors that have a halo
5947 // representation of the node
5948 // -----------------------------------------------------------------
5949
5950 // A map to know which nodes are already done
5951 std::map<Node*, bool> done_node;
5952
5953 // The storage for the halo nodes on face elements in this processor
5954 // with other processors
5955 Vector<Vector<Node*>> face_halo_node_pt(nproc);
5956
5957 // The storage for the ids of the halo nodes on face elements in
5958 // this processor with other processors
5959 Vector<Vector<unsigned>> face_halo_node_id(nproc);
5960
5961 // The storage for the haloed nodes on face elements in this
5962 // processor with other processors
5963 Vector<Vector<Node*>> face_haloed_node_pt(nproc);
5964
5965 // The storage for the ids of the haloed nodes on face elements in
5966 // this processor with other processors
5967 Vector<Vector<unsigned>> face_haloed_node_id(nproc);
5968
5969 // A map to know which nodes are face nodes and the processor in
5970 // charge is the current one
5971 std::map<Node*, bool> done_haloed_face_node;
5972
5973 // Go through all the face elements
5974 for (unsigned iface = 0; iface < nface_ele; iface++)
5975 {
5976 // Only work with the non halo face elements
5977 if (!is_halo_face_element[iface])
5978 {
5979 // Get the face element
5980 FiniteElement* ele_face_pt = face_ele_pt[iface];
5981 // The number of nodes of the face elements
5982 const unsigned nnodes = ele_face_pt->nnode();
5983 // Go through all the nodes in the face element
5984 for (unsigned in = 0; in < nnodes; in++)
5985 {
5986 Node* face_node_pt = ele_face_pt->node_pt(in);
5987 // Check if node is done
5988 if (!done_node[face_node_pt])
5989 {
5990 // Mark the node as done
5991 done_node[face_node_pt] = true;
5992 // First check if the node is halo
5993 if (face_node_pt->is_halo())
5994 {
5995 // Get the processor in charge for the current node
5996 int int_nonhalo_ID = face_node_pt->non_halo_proc_ID();
5997#ifdef PARANOID
5998 if (int_nonhalo_ID < 0)
5999 {
6000 std::ostringstream error_message;
6001 error_message
6002 << "The node was marked to be halo but the processor in "
6003 << "charge was found to be -1\n\n";
6004 throw OomphLibError(
6005 error_message.str(),
6006 "TriangleMesh::synchronize_boundary_coordinates()",
6007 OOMPH_EXCEPTION_LOCATION);
6008 }
6009#endif
6010 const unsigned ip = static_cast<unsigned>(int_nonhalo_ID);
6011 // Add the node to the structure that holds the halo
6012 // nodes, the current processor will need to send the
6013 // info. to the processor in charge.
6014 face_halo_node_pt[ip].push_back(face_node_pt);
6015 // ... finally look for the halo id with the processor in
6016 // charge
6017#ifdef PARANOID
6018 bool found_halo_node = false;
6019#endif
6020 const unsigned nhalo_iproc = this->nhalo_node(ip);
6021 for (unsigned ihn = 0; ihn < nhalo_iproc; ihn++)
6022 {
6023 Node* compare_face_node_pt = this->halo_node_pt(ip, ihn);
6024 if (compare_face_node_pt == face_node_pt)
6025 {
6026 // Once found the id of the node with the processor
6027 // store the id in the proper storage
6028 face_halo_node_id[ip].push_back(ihn);
6029#ifdef PARANOID
6030 // Set the flag to mark as found the halo node
6031 found_halo_node = true;
6032#endif
6033 // Break the loop
6034 break;
6035 }
6036 } // for (ih < nhalo_iproc)
6037#ifdef PARANOID
6038 if (!found_halo_node)
6039 {
6040 std::ostringstream error_message;
6041 error_message
6042 << "The halo id of the current node: (" << face_node_pt->x(0)
6043 << ", " << face_node_pt->x(1) << ") with processor (" << ip
6044 << ") was not found!!!\n\n";
6045 throw OomphLibError(
6046 error_message.str(),
6047 "TriangleMesh::synchronize_boundary_coordinates()",
6048 OOMPH_EXCEPTION_LOCATION);
6049 }
6050#endif
6051 } // if (face_node_pt->is_halo())
6052 // If the node is not halo then it could be haloed. If that
6053 // is the case then store the processors at which the node
6054 // is haloed and its id. The info. of these nodes will be
6055 // sent to all the processors with a halo counterpart
6056 else
6057 {
6058 for (unsigned ip = 0; ip < nproc; ip++)
6059 {
6060 // Only work with processors different that the current one
6061 if (ip != my_rank)
6062 {
6063 // If the node is found to be haloed with the "ip"
6064 // processor then save the haloed id in the storage.
6065 // The current processor needs to send info. to the
6066 // other processors to establish the boundary
6067 // coordinates
6068
6069 // Get the number of haloed nodes with processor ip
6070 const unsigned nhaloed_iproc = this->nhaloed_node(ip);
6071 for (unsigned ihdn = 0; ihdn < nhaloed_iproc; ihdn++)
6072 {
6073 Node* compare_face_node_pt = this->haloed_node_pt(ip, ihdn);
6074 if (face_node_pt == compare_face_node_pt)
6075 {
6076 // Store the node on the haloed node vector for
6077 // the corresponding processor
6078 face_haloed_node_pt[ip].push_back(face_node_pt);
6079 // Now store the halo id of the node with the
6080 // current processor
6081 face_haloed_node_id[ip].push_back(ihdn);
6082 // Mark the node as haloed with other processors,
6083 // so we know the processor in charge is the
6084 // current one "my_rank".
6085 done_haloed_face_node[face_node_pt] = true;
6086 // Break looking in the current processor, look in
6087 // the next one
6088 break;
6089 } // if (face_node_pt == compare_face_node_pt)
6090 } // for (ihdn < nhaloed_node_iproc)
6091 } // if (ip != my_rank)
6092 } // for (ip < nproc)
6093 } // else (non halo node)
6094 } // if (!done_node[node_face_pt])
6095 } // for (in < nnodes)
6096 } // if (!is_halo_face_element[iface])
6097 } // for (iface < nface_ele)
6098
6099 // -----------------------------------------------------------------
6100 // Fourth: Go through the halo nodes, package and send the
6101 // info. necessary to identify the face nodes in the processor in
6102 // charge. Identify the haloed nodes in the processor in charge and
6103 // establish the boundary coordinates, check if those nodes are
6104 // (already) marked as faced nodes, if that is the case then do not
6105 // establish the boundary coordinates but register them to send back
6106 // the info. to all the processors that have a halo representation
6107 // of the face node
6108 // -----------------------------------------------------------------
6109
6110 // Go through all processors
6111 for (unsigned ip = 0; ip < nproc; ip++)
6112 {
6113 // Only work with processors different than the current one
6114 if (ip != my_rank)
6115 {
6116 const unsigned nhalo_face_nodes = face_halo_node_pt[ip].size();
6117#ifdef PARANOID
6118 if (nhalo_face_nodes != face_halo_node_id[ip].size())
6119 {
6120 std::ostringstream error_message;
6121 error_message
6122 << "The number of found halo face nodes (" << nhalo_face_nodes
6123 << ") is different from the number of\nfound halo face ids ("
6124 << face_halo_node_id[ip].size() << ")!!!\n\n";
6125 throw OomphLibError(
6126 error_message.str(),
6127 "TriangleMesh::synchronize_boundary_coordinates()",
6128 OOMPH_EXCEPTION_LOCATION);
6129 }
6130#endif
6131
6132 // Container to send the info. related with the halo nodes to be
6133 // identified in the processors in charge
6134 Vector<unsigned> flat_unsigned_send_packed_data;
6135 Vector<double> flat_double_send_packed_data;
6136
6137 // Go through the halo face nodes in the "ip" processor
6138 for (unsigned ihfn = 0; ihfn < nhalo_face_nodes; ihfn++)
6139 {
6140 // Get the "ihfn"-th face node with the "ip" processor
6141 Node* halo_face_node_pt = face_halo_node_pt[ip][ihfn];
6142 // Get the halo id with the "ip" processor
6143 const unsigned halo_id = face_halo_node_id[ip][ihfn];
6144 // Get the boundary coordinate of the node
6145 Vector<double> zeta(1);
6146 halo_face_node_pt->get_coordinates_on_boundary(b, zeta);
6147 // Store the info. in the containers
6148 flat_unsigned_send_packed_data.push_back(halo_id);
6149 flat_double_send_packed_data.push_back(zeta[0]);
6150 }
6151
6152 // Send the info.
6153 MPI_Status status;
6154 MPI_Request request;
6155
6156 // Processor to which send the info
6157 int send_proc = static_cast<int>(ip);
6158 // Processor from which receive the info
6159 int receive_proc = static_cast<int>(ip);
6160
6161 // Storage to receive the info.
6162 Vector<unsigned> flat_unsigned_receive_packed_data;
6163 Vector<double> flat_double_receive_packed_data;
6164
6165 // --------------
6166 // Unsigned data
6167 unsigned nflat_unsigned_send = flat_unsigned_send_packed_data.size();
6168 MPI_Isend(&nflat_unsigned_send,
6169 1,
6170 MPI_UNSIGNED,
6171 send_proc,
6172 1,
6173 comm_pt->mpi_comm(),
6174 &request);
6175
6176 unsigned nflat_unsigned_receive = 0;
6177 MPI_Recv(&nflat_unsigned_receive,
6178 1,
6179 MPI_UNSIGNED,
6180 receive_proc,
6181 1,
6182 comm_pt->mpi_comm(),
6183 &status);
6184
6185 MPI_Wait(&request, MPI_STATUS_IGNORE);
6186
6187 if (nflat_unsigned_send != 0)
6188 {
6189 MPI_Isend(&flat_unsigned_send_packed_data[0],
6190 nflat_unsigned_send,
6191 MPI_UNSIGNED,
6192 send_proc,
6193 2,
6194 comm_pt->mpi_comm(),
6195 &request);
6196 }
6197
6198 if (nflat_unsigned_receive != 0)
6199 {
6200 flat_unsigned_receive_packed_data.resize(nflat_unsigned_receive);
6201 MPI_Recv(&flat_unsigned_receive_packed_data[0],
6202 nflat_unsigned_receive,
6203 MPI_UNSIGNED,
6204 receive_proc,
6205 2,
6206 comm_pt->mpi_comm(),
6207 &status);
6208 }
6209
6210 if (nflat_unsigned_send != 0)
6211 {
6212 MPI_Wait(&request, MPI_STATUS_IGNORE);
6213 }
6214
6215 // --------------
6216 // Double data
6217 unsigned nflat_double_send = flat_double_send_packed_data.size();
6218 MPI_Isend(&nflat_double_send,
6219 1,
6220 MPI_DOUBLE,
6221 send_proc,
6222 3,
6223 comm_pt->mpi_comm(),
6224 &request);
6225
6226 unsigned nflat_double_receive = 0;
6227 MPI_Recv(&nflat_double_receive,
6228 1,
6229 MPI_DOUBLE,
6230 receive_proc,
6231 3,
6232 comm_pt->mpi_comm(),
6233 &status);
6234
6235 MPI_Wait(&request, MPI_STATUS_IGNORE);
6236
6237 if (nflat_double_send != 0)
6238 {
6239 MPI_Isend(&flat_double_send_packed_data[0],
6240 nflat_double_send,
6241 MPI_DOUBLE,
6242 send_proc,
6243 4,
6244 comm_pt->mpi_comm(),
6245 &request);
6246 }
6247
6248 if (nflat_double_receive != 0)
6249 {
6250 flat_double_receive_packed_data.resize(nflat_double_receive);
6251 MPI_Recv(&flat_double_receive_packed_data[0],
6252 nflat_double_receive,
6253 MPI_DOUBLE,
6254 receive_proc,
6255 4,
6256 comm_pt->mpi_comm(),
6257 &status);
6258 }
6259
6260 if (nflat_double_send != 0)
6261 {
6262 MPI_Wait(&request, MPI_STATUS_IGNORE);
6263 }
6264 // --------------
6265
6266#ifdef PARANOID
6267 if (nflat_unsigned_receive != nflat_double_receive)
6268 {
6269 std::ostringstream error_message;
6270 error_message << "The number of unsigned received data ("
6271 << nflat_unsigned_receive << ") is different from the "
6272 << "number\nof double received data ("
6273 << nflat_double_receive << ")!!!\n\n";
6274 throw OomphLibError(
6275 error_message.str(),
6276 "TriangleMesh::synchronize_boundary_coordinates()",
6277 OOMPH_EXCEPTION_LOCATION);
6278 }
6279#endif
6280
6281 // With the received info. establish the boundary coordinates
6282 // for the face nodes that this processor is in charge (haloed
6283 // nodes)
6284 for (unsigned iflat_packed = 0; iflat_packed < nflat_unsigned_receive;
6285 iflat_packed++)
6286 {
6287 // Get the haloed id for the node
6288 const unsigned haloed_id =
6289 flat_unsigned_receive_packed_data[iflat_packed];
6290 // Get the boundary coordinates
6291 Vector<double> zeta(1);
6292 zeta[0] = flat_double_receive_packed_data[iflat_packed];
6293
6294 // Get the haloed node
6295 Node* haloed_face_node_pt = this->haloed_node_pt(ip, haloed_id);
6296
6297 // If the node has already set the boundary coordinates then
6298 // do not establish it. This is the case for the nodes that
6299 // lie on the boundary, for those nodes not identified on the
6300 // boundary since no elements lie on the boundary but the node
6301 // is on the boundary (a corner of an element lies on the
6302 // boundary) set boundary coordinates and register them to
6303 // send their info. to the processors with a halo counterpart
6304
6305 // If the node is not haloed face in the procesor in charge
6306 // then set the boundary coordinates and register the node to
6307 // send back the boundary coordinates to the processors with a
6308 // halo counterpart
6309 if (!done_haloed_face_node[haloed_face_node_pt])
6310 {
6311 // Establish the boundary coordinates
6312 haloed_face_node_pt->set_coordinates_on_boundary(b, zeta);
6313
6314 // Look in all processors where the node could be halo
6315 for (unsigned iiproc = 0; iiproc < nproc; iiproc++)
6316 {
6317 // Only work with processors different than the current one
6318 if (iiproc != my_rank)
6319 {
6320 // Get the number of haloed nodes with processor iiproc
6321 const unsigned nhaloed_node_iiproc = this->nhaloed_node(iiproc);
6322 for (unsigned ihdn = 0; ihdn < nhaloed_node_iiproc; ihdn++)
6323 {
6324 Node* compare_haloed_node_pt =
6325 this->haloed_node_pt(iiproc, ihdn);
6326 if (haloed_face_node_pt == compare_haloed_node_pt)
6327 {
6328 // Store the node on the haloed node vector for the
6329 // corresponding processor
6330 face_haloed_node_pt[iiproc].push_back(haloed_face_node_pt);
6331 // Now store the halo id of the node with the current
6332 // processor
6333 face_haloed_node_id[iiproc].push_back(ihdn);
6334 // Break searching in the current processor, search in
6335 // the next one
6336 break;
6337 } // if (haloed_face_node_pt==compare_haloed_face_node_pt)
6338 } // for (ihdn < nhaloed_node_iproc)
6339 } // if (iiproc != my_rank)
6340 } // for (iiproc < nproc)
6341 } // if (!done_haloed_face_node[haloed_face_node_pt])
6342 } // for (iflat_packed < nflat_unsigned_receive)
6343 } // if (ip != my_rank)
6344 } // for (ip < nproc)
6345
6346 // -----------------------------------------------------------------
6347 // Fifth: The boundary coordinates have been established in the
6348 // processors in charge of the nodes. Now each processor send back
6349 // the boundary coordinates to all the processors where there is a
6350 // halo representation of the node
6351 // -----------------------------------------------------------------
6352
6353 // Go through all processors
6354 for (unsigned ip = 0; ip < nproc; ip++)
6355 {
6356 // Only work with processors different than the current one
6357 if (ip != my_rank)
6358 {
6359 // Container to send the info. of the haloed nodes to all the
6360 // processors
6361 Vector<unsigned> flat_unsigned_send_packed_data;
6362 Vector<double> flat_double_send_packed_data;
6363
6364 // Get the total number of haloed face nodes with the "ip"
6365 // processor
6366 const unsigned nhaloed_face_nodes = face_haloed_node_pt[ip].size();
6367 // Go through the haloed face nodes in the "ip" processor
6368 for (unsigned ihdfn = 0; ihdfn < nhaloed_face_nodes; ihdfn++)
6369 {
6370 // Get the "ihdfn"-th face node with the "ip" processor
6371 Node* haloed_face_node_pt = face_haloed_node_pt[ip][ihdfn];
6372 // Get the haloed id with the "ip" processor
6373 const unsigned haloed_id = face_haloed_node_id[ip][ihdfn];
6374 // Get the boundary coordinate of the node
6375 Vector<double> zeta(1);
6376 haloed_face_node_pt->get_coordinates_on_boundary(b, zeta);
6377 // Store the info. in the containers
6378 flat_unsigned_send_packed_data.push_back(haloed_id);
6379 flat_double_send_packed_data.push_back(zeta[0]);
6380 }
6381
6382 // Send the info.
6383 MPI_Status status;
6384 MPI_Request request;
6385
6386 // Processor to which send the info
6387 int send_proc = static_cast<int>(ip);
6388 // Processor from which receive the info
6389 int receive_proc = static_cast<int>(ip);
6390
6391 // Storage to receive the info.
6392 Vector<unsigned> flat_unsigned_receive_packed_data;
6393 Vector<double> flat_double_receive_packed_data;
6394
6395 // --------------
6396 // Unsigned data
6397 unsigned nflat_unsigned_send = flat_unsigned_send_packed_data.size();
6398 MPI_Isend(&nflat_unsigned_send,
6399 1,
6400 MPI_UNSIGNED,
6401 send_proc,
6402 1,
6403 comm_pt->mpi_comm(),
6404 &request);
6405
6406 unsigned nflat_unsigned_receive = 0;
6407 MPI_Recv(&nflat_unsigned_receive,
6408 1,
6409 MPI_UNSIGNED,
6410 receive_proc,
6411 1,
6412 comm_pt->mpi_comm(),
6413 &status);
6414
6415 MPI_Wait(&request, MPI_STATUS_IGNORE);
6416
6417 if (nflat_unsigned_send != 0)
6418 {
6419 MPI_Isend(&flat_unsigned_send_packed_data[0],
6420 nflat_unsigned_send,
6421 MPI_UNSIGNED,
6422 send_proc,
6423 2,
6424 comm_pt->mpi_comm(),
6425 &request);
6426 }
6427
6428 if (nflat_unsigned_receive != 0)
6429 {
6430 flat_unsigned_receive_packed_data.resize(nflat_unsigned_receive);
6431 MPI_Recv(&flat_unsigned_receive_packed_data[0],
6432 nflat_unsigned_receive,
6433 MPI_UNSIGNED,
6434 receive_proc,
6435 2,
6436 comm_pt->mpi_comm(),
6437 &status);
6438 }
6439
6440 if (nflat_unsigned_send != 0)
6441 {
6442 MPI_Wait(&request, MPI_STATUS_IGNORE);
6443 }
6444
6445 // --------------
6446 // Double data
6447 unsigned nflat_double_send = flat_double_send_packed_data.size();
6448 MPI_Isend(&nflat_double_send,
6449 1,
6450 MPI_DOUBLE,
6451 send_proc,
6452 3,
6453 comm_pt->mpi_comm(),
6454 &request);
6455
6456 unsigned nflat_double_receive = 0;
6457 MPI_Recv(&nflat_double_receive,
6458 1,
6459 MPI_DOUBLE,
6460 receive_proc,
6461 3,
6462 comm_pt->mpi_comm(),
6463 &status);
6464
6465 MPI_Wait(&request, MPI_STATUS_IGNORE);
6466
6467 if (nflat_double_send != 0)
6468 {
6469 MPI_Isend(&flat_double_send_packed_data[0],
6470 nflat_double_send,
6471 MPI_DOUBLE,
6472 send_proc,
6473 4,
6474 comm_pt->mpi_comm(),
6475 &request);
6476 }
6477
6478 if (nflat_double_receive != 0)
6479 {
6480 flat_double_receive_packed_data.resize(nflat_double_receive);
6481 MPI_Recv(&flat_double_receive_packed_data[0],
6482 nflat_double_receive,
6483 MPI_DOUBLE,
6484 receive_proc,
6485 4,
6486 comm_pt->mpi_comm(),
6487 &status);
6488 }
6489
6490 if (nflat_double_send != 0)
6491 {
6492 MPI_Wait(&request, MPI_STATUS_IGNORE);
6493 }
6494 // --------------
6495
6496#ifdef PARANOID
6497 if (nflat_unsigned_receive != nflat_double_receive)
6498 {
6499 std::ostringstream error_message;
6500 error_message << "The number of unsigned received data ("
6501 << nflat_unsigned_receive << ") is different from the "
6502 << "number\nof double received data ("
6503 << nflat_double_receive << ")!!!\n\n";
6504 throw OomphLibError(
6505 error_message.str(),
6506 "TriangleMesh::synchronize_boundary_coordinates()",
6507 OOMPH_EXCEPTION_LOCATION);
6508 }
6509#endif
6510
6511 // With the received info. establish the boundary coordinates
6512 // received for the face nodes that this processor is not in
6513 // charge (halo nodes)
6514 for (unsigned iflat_packed = 0; iflat_packed < nflat_unsigned_receive;
6515 iflat_packed++)
6516 {
6517 // Get the halo id for the node
6518 const unsigned halo_id =
6519 flat_unsigned_receive_packed_data[iflat_packed];
6520 // Get the boundary coordinates
6521 Vector<double> zeta(1);
6522 zeta[0] = flat_double_receive_packed_data[iflat_packed];
6523
6524 // Get the halo node
6525 Node* halo_face_node_pt = this->halo_node_pt(ip, halo_id);
6526
6527 // It could be possible that the node has been already
6528 // established boundary coordinates since it is a halo face
6529 // node. However, for those elements not on the boundary, but
6530 // having a corner node on the boundary this procedure will
6531 // establish boundary coordinates for those nodes
6532
6533 // this->add_boundary_node(b, halo_face_node_pt);
6534
6535 // Establish the boundary coordinates
6536 halo_face_node_pt->set_coordinates_on_boundary(b, zeta);
6537 } // for (iflat_packed < nflat_unsigned_receive)
6538 } // if (ip != my_rank)
6539 } // for (ip < nproc)
6540
6541 // Clean all the created face elements
6542 for (unsigned ie = 0; ie < nbound_ele; ie++)
6543 {
6544 delete tmp_face_ele_pt[ie];
6545 tmp_face_ele_pt[ie] = 0;
6546 }
6547
6548 // Now get a new face mesh representation and fill the data for those
6549 // processors with halo segments
6550 if (is_internal_boundary)
6551 {
6552 re_scale_re_assigned_initial_zeta_values_for_internal_boundary(b);
6553 }
6554 }
6555
6556 //======================================================================
6557 /// Re-assign the boundary segments initial zeta (arclength)
6558 /// for those internal boundaries that were splited during the
6559 /// distribution process (only apply for internal boundaries that
6560 /// have one face element at each side of the boundary)
6561 //======================================================================
6562 template<class ELEMENT>
6565 const unsigned& b)
6566 {
6567 // ------------------------------------------------------------------
6568 // First: Get the face elements associated with the current boundary
6569 // Only include nonhalo face elements
6570 // ------------------------------------------------------------------
6571 // Temporary storage for face elements
6572 Vector<FiniteElement*> face_el_pt;
6573
6574 // Temporary storage for the number of elements adjacent to the
6575 // boundary
6576 unsigned nele = 0;
6577
6578 // Temporary storage for elements adjacent to the boundary that have
6579 // a common edge (related with internal boundaries)
6580 unsigned n_repeated_ele = 0;
6581
6582 const unsigned n_regions = this->nregion();
6583
6584 // Temporary storage for already done nodes
6585 Vector<std::pair<Node*, Node*>> done_nodes_pt;
6586
6587 // If there is more than one region then only use boundary
6588 // coordinates from the bulk side (region 0)
6589 if (n_regions > 1)
6590 {
6591 for (unsigned rr = 0; rr < n_regions; rr++)
6592 {
6593 const unsigned region_id =
6594 static_cast<unsigned>(this->Region_attribute[rr]);
6595
6596 // Loop over all elements on boundaries in region i_r
6597 const unsigned nel_in_region =
6598 this->nboundary_element_in_region(b, region_id);
6599
6600 unsigned nel_repetead_in_region = 0;
6601
6602 // Only bother to do anything else, if there are elements
6603 // associated with the boundary and the current region
6604 if (nel_in_region > 0)
6605 {
6606 bool repeated = false;
6607
6608 // Loop over the bulk elements adjacent to boundary b
6609 for (unsigned e = 0; e < nel_in_region; e++)
6610 {
6611 // Get pointer to the bulk element that is adjacent to
6612 // boundary b
6613 FiniteElement* bulk_elem_pt =
6614 this->boundary_element_in_region_pt(b, region_id, e);
6615
6616 // Remember only to work with nonhalo elements
6617 if (bulk_elem_pt->is_halo())
6618 {
6619 n_repeated_ele++;
6620 continue;
6621 }
6622
6623 // Find the index of the face of element e along boundary b
6624 int face_index =
6625 this->face_index_at_boundary_in_region(b, region_id, e);
6626
6627 // Before adding the new element we need to be sure that the
6628 // edge that this element represent has not been already
6629 // added
6630 FiniteElement* tmp_ele_pt =
6631 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6632
6633 const unsigned n_nodes = tmp_ele_pt->nnode();
6634
6635 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6636 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6637
6638 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6639 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
6640
6641 // Search for repeated nodes
6642 const unsigned n_done_nodes = done_nodes_pt.size();
6643 for (unsigned l = 0; l < n_done_nodes; l++)
6644 {
6645 if (tmp_pair == done_nodes_pt[l] ||
6646 tmp_pair_inverse == done_nodes_pt[l])
6647 {
6648 nel_repetead_in_region++;
6649 repeated = true;
6650 break;
6651 }
6652 }
6653
6654 // Create new face element
6655 if (!repeated)
6656 {
6657 // Add the pair of nodes (edge) to the node dones
6658 done_nodes_pt.push_back(tmp_pair);
6659 // Add the element to the face elements
6660 face_el_pt.push_back(tmp_ele_pt);
6661 }
6662 else
6663 {
6664 // Clean up
6665 delete tmp_ele_pt;
6666 tmp_ele_pt = 0;
6667 }
6668
6669 // Re-start
6670 repeated = false;
6671
6672 } // for nel
6673
6674 nele += nel_in_region;
6675
6676 n_repeated_ele += nel_repetead_in_region;
6677
6678 } // if (nel_in_region > 0)
6679 } // for (rr < n_regions)
6680 } // if (n_regions > 1)
6681 // Otherwise it's just the normal boundary functions
6682 else
6683 {
6684 // Loop over all elements on boundaries
6685 nele = this->nboundary_element(b);
6686
6687 // Only bother to do anything else, if there are elements
6688 if (nele > 0)
6689 {
6690 // Check for repeated ones
6691 bool repeated = false;
6692
6693 // Loop over the bulk elements adjacent to boundary b
6694 for (unsigned e = 0; e < nele; e++)
6695 {
6696 // Get pointer to the bulk element that is adjacent to
6697 // boundary b
6698 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
6699
6700 // Remember only to work with nonhalo elements
6701 if (bulk_elem_pt->is_halo())
6702 {
6703 n_repeated_ele++;
6704 // Skip the halo element
6705 continue;
6706 }
6707
6708 // Find the index of the face of element e along boundary b
6709 int face_index = this->face_index_at_boundary(b, e);
6710
6711 // Before adding the new element we need to be sure that the
6712 // edge that this element represents has not been already
6713 // added (only applies for internal boundaries)
6714 FiniteElement* tmp_ele_pt =
6715 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
6716
6717 const unsigned n_nodes = tmp_ele_pt->nnode();
6718
6719 std::pair<Node*, Node*> tmp_pair = std::make_pair(
6720 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
6721
6722 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
6723 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
6724
6725 // Search for repeated nodes
6726 const unsigned n_done_nodes = done_nodes_pt.size();
6727 for (unsigned l = 0; l < n_done_nodes; l++)
6728 {
6729 if (tmp_pair == done_nodes_pt[l] ||
6730 tmp_pair_inverse == done_nodes_pt[l])
6731 {
6732 // Increase the number of repeated elements
6733 n_repeated_ele++;
6734 // Mark the element as repeated
6735 repeated = true;
6736 break;
6737 }
6738 }
6739
6740 // Create new face element
6741 if (!repeated)
6742 {
6743 // Add the pair of nodes (edge) to the node dones
6744 done_nodes_pt.push_back(tmp_pair);
6745 // Add the element to the face elements
6746 face_el_pt.push_back(tmp_ele_pt);
6747 }
6748 else
6749 {
6750 // Free the repeated bulk element!!
6751 delete tmp_ele_pt;
6752 tmp_ele_pt = 0;
6753 }
6754
6755 // Re-start
6756 repeated = false;
6757
6758 } // for (e < nel)
6759 } // if (nel > 0)
6760
6761 } // else (n_regions > 1)
6762
6763 // Do not consider the repeated elements
6764 nele -= n_repeated_ele;
6765
6766#ifdef PARANOID
6767 if (nele != face_el_pt.size())
6768 {
6769 std::ostringstream error_message;
6770 error_message
6771 << "The independet counting of face elements (" << nele << ") for "
6772 << "boundary (" << b << ") is different\n"
6773 << "from the real number of face elements in the container ("
6774 << face_el_pt.size() << ")\n";
6775 //<< "Possible memory leak\n"
6776 throw OomphLibError(error_message.str(),
6777 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6778 "values_for_internal_boundary()",
6779 OOMPH_EXCEPTION_LOCATION);
6780 }
6781#endif
6782
6783 // ----------------------------------------------------------------
6784 // Second: Sort the face elements (to create segments), only
6785 // consider nonhalo elements
6786 // ----------------------------------------------------------------
6787
6788 // Get the total number of nonhalo face elements
6789 const unsigned nnon_halo_face_elements = face_el_pt.size();
6790
6791 // The vector of list to store the "segments" that compound the
6792 // boundary (segments may appear only in a distributed mesh)
6793 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
6794
6795 // Number of already sorted face elements
6796 unsigned nsorted_face_elements = 0;
6797
6798 // Keep track of who's done
6799 std::map<FiniteElement*, bool> done_el;
6800
6801 // Keep track of which element is inverted
6802 std::map<FiniteElement*, bool> is_inverted;
6803
6804 // Iterate until all possible segments have been created
6805 while (nsorted_face_elements < nnon_halo_face_elements)
6806 {
6807 // The ordered list of face elements (in a distributed mesh a
6808 // collection of contiguous face elements define a segment)
6809 std::list<FiniteElement*> sorted_el_pt;
6810
6811#ifdef PARANOID
6812 // Select an initial element for the segment
6813 bool found_initial_face_element = false;
6814#endif
6815
6816 FiniteElement* ele_face_pt = 0;
6817
6818 unsigned iface = 0;
6819 for (iface = 0; iface < nele; iface++)
6820 {
6821 ele_face_pt = face_el_pt[iface];
6822 // If not done then take it as initial face element
6823 if (!done_el[ele_face_pt])
6824 {
6825#ifdef PARANOID
6826 found_initial_face_element = true;
6827#endif
6828 nsorted_face_elements++;
6829 iface++; // The next element number
6830 sorted_el_pt.push_back(ele_face_pt);
6831 // Mark as done
6832 done_el[ele_face_pt] = true;
6833 break;
6834 }
6835 } // for (iface < nele)
6836
6837#ifdef PARANOID
6838 if (!found_initial_face_element)
6839 {
6840 std::ostringstream error_message;
6841 error_message
6842 << "Could not find an initial face element for the current segment\n";
6843 // << "----- Possible memory leak -----\n";
6844 throw OomphLibError(error_message.str(),
6845 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6846 "values_for_internal_boundary()",
6847 OOMPH_EXCEPTION_LOCATION);
6848 }
6849#endif
6850
6851 // Number of nodes
6852 const unsigned nnod = ele_face_pt->nnode();
6853
6854 // Left and rightmost nodes (the left and right nodes of the
6855 // current face element)
6856 Node* left_node_pt = ele_face_pt->node_pt(0);
6857 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
6858
6859 // Continue iterating if a new face element has been added to the
6860 // list
6861 bool face_element_added = false;
6862
6863 // While a new face element has been added to the set of sorted
6864 // face elements then re-iterate
6865 do
6866 {
6867 // Start from the next face element since we have already added
6868 // the previous one as the initial face element (any previous
6869 // face element had to be added on previous iterations)
6870 for (unsigned iiface = iface; iiface < nele; iiface++)
6871 {
6872 // Re-start flag
6873 face_element_added = false;
6874
6875 // Get the candidate element
6876 ele_face_pt = face_el_pt[iiface];
6877
6878 // Check that the candidate element has not been done
6879 if (!(done_el[ele_face_pt]))
6880 {
6881 // Get the left and right nodes of the current element
6882 Node* local_left_node_pt = ele_face_pt->node_pt(0);
6883 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
6884
6885 // New element fits at the left of segment and is not inverted
6886 if (left_node_pt == local_right_node_pt)
6887 {
6888 left_node_pt = local_left_node_pt;
6889 sorted_el_pt.push_front(ele_face_pt);
6890 is_inverted[ele_face_pt] = false;
6891 face_element_added = true;
6892 }
6893 // New element fits at the left of segment and is inverted
6894 else if (left_node_pt == local_left_node_pt)
6895 {
6896 left_node_pt = local_right_node_pt;
6897 sorted_el_pt.push_front(ele_face_pt);
6898 is_inverted[ele_face_pt] = true;
6899 face_element_added = true;
6900 }
6901 // New element fits on the right of segment and is not inverted
6902 else if (right_node_pt == local_left_node_pt)
6903 {
6904 right_node_pt = local_right_node_pt;
6905 sorted_el_pt.push_back(ele_face_pt);
6906 is_inverted[ele_face_pt] = false;
6907 face_element_added = true;
6908 }
6909 // New element fits on the right of segment and is inverted
6910 else if (right_node_pt == local_right_node_pt)
6911 {
6912 right_node_pt = local_left_node_pt;
6913 sorted_el_pt.push_back(ele_face_pt);
6914 is_inverted[ele_face_pt] = true;
6915 face_element_added = true;
6916 }
6917
6918 if (face_element_added)
6919 {
6920 done_el[ele_face_pt] = true;
6921 nsorted_face_elements++;
6922 break;
6923 }
6924
6925 } // if (!(done_el[ele_face_pt]))
6926 } // for (iiface<nnon_halo_face_element)
6927 } while (face_element_added &&
6928 (nsorted_face_elements < nnon_halo_face_elements));
6929
6930 // Store the created segment in the vector of segments
6931 segment_sorted_ele_pt.push_back(sorted_el_pt);
6932
6933 } // while(nsorted_face_elements < nnon_halo_face_elements);
6934
6935 // --------------------------------------------------------------
6936 // Third: We have the face elements sorted, now assign boundary
6937 // coordinates to the nodes in the segments and compute the
6938 // arclength of the segment
6939 // --------------------------------------------------------------
6940
6941 // Vector of sets that stores the nodes of each segment based on a
6942 // lexicographically order starting from the bottom left node of
6943 // each segment
6944 Vector<std::set<Node*>> segment_all_nodes_pt;
6945
6946 // The number of segments in this processor
6947 const unsigned nsegments = segment_sorted_ele_pt.size();
6948
6949#ifdef PARANOID
6950 if (nnon_halo_face_elements > 0 && nsegments == 0)
6951 {
6952 std::ostringstream error_message;
6953 error_message
6954 << "The number of segments is zero, but the number of nonhalo\n"
6955 << "elements is: (" << nnon_halo_face_elements << ")\n";
6956 throw OomphLibError(error_message.str(),
6957 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6958 "values_for_internal_boundary()",
6959 OOMPH_EXCEPTION_LOCATION);
6960 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
6961#endif
6962
6963 // The arclength of each segment in the current processor
6964 Vector<double> segment_arclength(nsegments);
6965
6966 // The initial zeta for the segment
6967 Vector<double> initial_zeta_segment(nsegments);
6968
6969 // The final zeta for the segment
6970 Vector<double> final_zeta_segment(nsegments);
6971
6972 // Go through all the segments and compute the LOCAL boundary
6973 // coordinates
6974 for (unsigned is = 0; is < nsegments; is++)
6975 {
6976#ifdef PARANOID
6977 if (segment_sorted_ele_pt[is].size() == 0)
6978 {
6979 std::ostringstream error_message;
6980 error_message << "The (" << is << ")-th segment has no elements\n";
6981 throw OomphLibError(error_message.str(),
6982 "TriangleMesh::re_scale_re_assigned_initial_zeta_"
6983 "values_for_internal_boundary()",
6984 OOMPH_EXCEPTION_LOCATION);
6985 } // if (segment_sorted_ele_pt[is].size() == 0)
6986#endif
6987
6988 // Get access to the first element on the segment
6989 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
6990
6991 // Number of nodes
6992 const unsigned nnod = first_ele_pt->nnode();
6993
6994 // Get the first node of the current segment
6995 Node* first_node_pt = first_ele_pt->node_pt(0);
6996 if (is_inverted[first_ele_pt])
6997 {
6998 first_node_pt = first_ele_pt->node_pt(nnod - 1);
6999 }
7000
7001 // Get access to the last element on the segment
7002 FiniteElement* last_ele_pt = segment_sorted_ele_pt[is].back();
7003
7004 // Get the last node of the current segment
7005 Node* last_node_pt = last_ele_pt->node_pt(nnod - 1);
7006 if (is_inverted[last_ele_pt])
7007 {
7008 last_node_pt = last_ele_pt->node_pt(0);
7009 }
7010
7011 // Coordinates of left node
7012 double x_left = first_node_pt->x(0);
7013 double y_left = first_node_pt->x(1);
7014
7015 // Initialise boundary coordinate (local boundary coordinate for
7016 // boundaries with more than one segment)
7017 Vector<double> zeta(1, 0.0);
7018
7019 // If we have associated a GeomObject then it is not necessary to
7020 // compute the arclength, only read the values from the nodes at
7021 // the edges
7022 if (this->boundary_geom_object_pt(b) != 0)
7023 {
7024 first_node_pt->get_coordinates_on_boundary(b, zeta);
7025 initial_zeta_segment[is] = zeta[0];
7026 last_node_pt->get_coordinates_on_boundary(b, zeta);
7027 final_zeta_segment[is] = zeta[0];
7028 }
7029
7030 // Lexicographically bottom left node
7031 std::set<Node*> local_nodes_pt;
7032 local_nodes_pt.insert(first_node_pt);
7033
7034 // Now loop over nodes in order
7035 for (std::list<FiniteElement*>::iterator it =
7036 segment_sorted_ele_pt[is].begin();
7037 it != segment_sorted_ele_pt[is].end();
7038 it++)
7039 {
7040 // Get element
7041 FiniteElement* el_pt = *it;
7042
7043 // Start node and increment
7044 unsigned k_nod = 1;
7045 int nod_diff = 1;
7046 if (is_inverted[el_pt])
7047 {
7048 k_nod = nnod - 2;
7049 nod_diff = -1;
7050 }
7051
7052 // Loop over nodes
7053 for (unsigned j = 1; j < nnod; j++)
7054 {
7055 Node* nod_pt = el_pt->node_pt(k_nod);
7056 k_nod += nod_diff;
7057
7058 // Coordinates of right node
7059 double x_right = nod_pt->x(0);
7060 double y_right = nod_pt->x(1);
7061
7062 // Increment boundary coordinate
7063 zeta[0] += sqrt((x_right - x_left) * (x_right - x_left) +
7064 (y_right - y_left) * (y_right - y_left));
7065
7066 // Increment reference coordinate
7067 x_left = x_right;
7068 y_left = y_right;
7069
7070 // Get lexicographically bottom left node but only
7071 // use vertex nodes as candidates
7072 local_nodes_pt.insert(nod_pt);
7073
7074 } // for (j < nnod)
7075 } // iterator over the elements in the segment
7076
7077 // Store the arclength of the segment
7078 segment_arclength[is] = zeta[0];
7079
7080 // Add the nodes for the corresponding segment in the container
7081 segment_all_nodes_pt.push_back(local_nodes_pt);
7082
7083 } // for (is < nsegments)
7084
7085 // ------------------------------------------------------------------
7086 // Fourth: Now we have the segments sorted, with arclength and with
7087 // LOCAL arclength assigned to the nodes. Procced to re-scale the
7088 // coordinates on the nodes based on the arclength
7089 // ------------------------------------------------------------------
7090
7091 // ------------------------------------------------------------------
7092 // Clear the original storages
7093 Boundary_segment_inverted[b].clear();
7094 Boundary_segment_initial_coordinate[b].clear();
7095 Boundary_segment_final_coordinate[b].clear();
7096
7097 Boundary_segment_initial_zeta[b].clear();
7098 Boundary_segment_final_zeta[b].clear();
7099
7100 Boundary_segment_initial_arclength[b].clear();
7101 Boundary_segment_final_arclength[b].clear();
7102
7103 // Get the zeta values for the first and last node in the boundary
7104 Vector<double> first_node_zeta_coordinate(1, 0.0);
7105 Vector<double> last_node_zeta_coordinate(1, 0.0);
7106 first_node_zeta_coordinate = boundary_initial_zeta_coordinate(b);
7107 last_node_zeta_coordinate = boundary_final_zeta_coordinate(b);
7108
7109 // Get the boundary arclength
7110 const double boundary_arclength =
7111 std::max(first_node_zeta_coordinate[0], last_node_zeta_coordinate[0]);
7112
7113 // Go through the segments and get the first and last node for each
7114 // segment
7115 for (unsigned is = 0; is < nsegments; is++)
7116 {
7117 // Get the first face element of the segment
7118 FiniteElement* first_face_ele_pt = segment_sorted_ele_pt[is].front();
7119
7120 // The number of nodes
7121 const unsigned nnod = first_face_ele_pt->nnode();
7122
7123 // ... and the first node of the segment
7124 Node* first_node_pt = first_face_ele_pt->node_pt(0);
7125 if (is_inverted[first_face_ele_pt])
7126 {
7127 first_node_pt = first_face_ele_pt->node_pt(nnod - 1);
7128 }
7129
7130 // Get the bound coordinates of the node
7131 Vector<double> zeta_first(1);
7132 first_node_pt->get_coordinates_on_boundary(b, zeta_first);
7133
7134 // Get the last face element of the segment
7135 FiniteElement* last_face_ele_pt = segment_sorted_ele_pt[is].back();
7136
7137 // ... and the last node of the segment
7138 Node* last_node_pt = last_face_ele_pt->node_pt(nnod - 1);
7139 if (is_inverted[last_face_ele_pt])
7140 {
7141 last_node_pt = last_face_ele_pt->node_pt(0);
7142 }
7143
7144 // Get the bound coordinates of the node
7145 Vector<double> zeta_last(1);
7146 last_node_pt->get_coordinates_on_boundary(b, zeta_last);
7147
7148 // Now that we have the first and last node of the segment, get
7149 // the coordinates of the nodes
7150 Vector<double> first_node_coord(2);
7151 Vector<double> last_node_coord(2);
7152 for (unsigned i = 0; i < 2; i++)
7153 {
7154 first_node_coord[i] = first_node_pt->x(i);
7155 last_node_coord[i] = last_node_pt->x(i);
7156 }
7157
7158 // Re-assign the values to identify the segments on the new mesh
7159 Boundary_segment_inverted[b].push_back(0);
7160 Boundary_segment_initial_coordinate[b].push_back(first_node_coord);
7161 Boundary_segment_final_coordinate[b].push_back(last_node_coord);
7162
7163 // Check if the boudary has an associated GeomObject
7164 if (this->boundary_geom_object_pt(b) != 0)
7165 {
7166 Boundary_segment_initial_zeta[b].push_back(zeta_first[0]);
7167 Boundary_segment_final_zeta[b].push_back(zeta_last[0]);
7168 }
7169 else
7170 {
7171 // Re-assign the values and re-scale them
7172 Boundary_segment_initial_arclength[b].push_back(zeta_first[0] *
7173 boundary_arclength);
7174 Boundary_segment_final_arclength[b].push_back(zeta_last[0] *
7175 boundary_arclength);
7176 }
7177
7178 } // for (is < nsegments)
7179
7180 // Clean all the created face elements
7181 for (unsigned i = 0; i < nele; i++)
7182 {
7183 delete face_el_pt[i];
7184 face_el_pt[i] = 0;
7185 }
7186 }
7187
7188#endif // OOMPH_HAS_MPI
7189
7190
7191#ifdef OOMPH_HAS_TRIANGLE_LIB
7192
7193 //========================================================================
7194 /// Create TriangulateIO object via the .poly file
7195 //========================================================================
7196 template<class ELEMENT>
7198 const std::string& poly_file_name,
7199 TriangulateIO& triangulate_io,
7200 bool& use_attributes)
7201 {
7202 // Process poly file
7203 // -----------------
7204 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
7205 if (!poly_file)
7206 {
7207 throw OomphLibError("Error opening .poly file\n",
7208 OOMPH_CURRENT_FUNCTION,
7209 OOMPH_EXCEPTION_LOCATION);
7210 }
7211
7212 // Initialize triangulateio structure
7214
7215 // Ignore the first line with structure description
7216 poly_file.ignore(80, '\n');
7217
7218 // Read and store number of nodes
7219 unsigned invertices;
7220 poly_file >> invertices;
7221 triangulate_io.numberofpoints = invertices;
7222
7223 // Initialisation of the point list
7224 triangulate_io.pointlist =
7225 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
7226
7227 // Read and store spatial dimension of nodes
7228 unsigned mesh_dim;
7229 poly_file >> mesh_dim;
7230
7231 if (mesh_dim == 0)
7232 {
7233 mesh_dim = 2;
7234 }
7235
7236#ifdef PARANOID
7237 if (mesh_dim != 2)
7238 {
7239 throw OomphLibError("The dimension must be 2\n",
7240 OOMPH_CURRENT_FUNCTION,
7241 OOMPH_EXCEPTION_LOCATION);
7242 }
7243#endif
7244
7245 // Read and check the flag for attributes
7246 unsigned nextras;
7247 poly_file >> nextras;
7248
7249 triangulate_io.numberofpointattributes = 0;
7250 triangulate_io.pointattributelist = (double*)NULL;
7251
7252 // Read and check the flag for boundary markers
7253 unsigned nodemarkers;
7254 poly_file >> nodemarkers;
7255 triangulate_io.pointmarkerlist = (int*)NULL;
7256
7257#ifdef PARANOID
7258 // Reading the .poly with the oomph.lib we need
7259 // to set the point attribute and markers to 0
7260 if (nextras != 0 || nodemarkers != 0)
7261 {
7262 oomph_info << "===================================================="
7263 << std::endl
7264 << std::endl;
7265 oomph_info << "Reading the .poly file via oomph_lib \n"
7266 << "point's attribute and point's markers \n"
7267 << "are automatically set to 0" << std::endl;
7268 oomph_info << "===================================================="
7269 << std::endl;
7270 }
7271#endif
7272
7273 // Dummy for node number (and attribute or markers if included)
7274 unsigned dummy_value;
7275 unsigned count_point = 0;
7276 std::string test_string;
7277
7278 // Skip line with commentary
7279 getline(poly_file, test_string, '#');
7280 poly_file.ignore(80, '\n');
7281
7282 // Read and store all the nodes coordinates
7283 // (hole's vertices as well)
7284 for (unsigned count = 0; count < invertices; count++)
7285 {
7286 poly_file >> dummy_value;
7287 poly_file >> triangulate_io.pointlist[count_point];
7288 poly_file >> triangulate_io.pointlist[count_point + 1];
7289 if (nextras != 0 || nodemarkers != 0)
7290 {
7291 for (unsigned j = 0; j < nextras; j++)
7292 {
7293 poly_file >> dummy_value;
7294 }
7295 }
7296 else if (nextras != 0 && nodemarkers != 0)
7297 {
7298 for (unsigned j = 0; j < nextras; j++)
7299 {
7300 poly_file >> dummy_value;
7301 poly_file >> dummy_value;
7302 }
7303 }
7304 // Read the next line
7305 poly_file.ignore(80, '\n');
7306
7307 // Skip line with commentary for internal box whether found
7308 if (poly_file.get() == '#')
7309 {
7310 poly_file.ignore(80, '\n');
7311 }
7312 // If read the char should be put back in the string
7313
7314 else
7315 {
7316 poly_file.unget();
7317 }
7318 count_point += 2;
7319 }
7320
7321 // The line with the segment's commentary has been skipped
7322 // by the command of the last loop
7323
7324 // Read and store the number of segments
7325 unsigned dummy_seg;
7326 unsigned inelements;
7327 poly_file >> inelements;
7328
7329 unsigned segment_markers;
7330 poly_file >> segment_markers;
7331
7332 // Marker list should be provided by the user to assign
7333 // each segment to a boundary
7334#ifdef PARANOID
7335 if (segment_markers != 1)
7336 {
7337 std::ostringstream error_stream;
7338 error_stream << "The segment marker should be provided \n"
7339 << "In order to assign each segment to a boundary \n "
7340 << std::endl;
7341
7342 throw OomphLibError(
7343 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
7344 }
7345#endif
7346
7347 triangulate_io.numberofsegments = inelements;
7348 triangulate_io.segmentlist =
7349 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
7350 triangulate_io.segmentmarkerlist =
7351 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
7352
7353 // Read all the segments edges and markers
7354 for (unsigned i = 0; i < 2 * inelements; i += 2)
7355 {
7356 poly_file >> dummy_seg;
7357 poly_file >> triangulate_io.segmentlist[i];
7358 poly_file >> triangulate_io.segmentlist[i + 1];
7359 if (segment_markers != 0)
7360 {
7361 poly_file >> triangulate_io.segmentmarkerlist[i / 2];
7362 }
7363
7364 // Skip line with commentary
7365 poly_file.ignore(80, '\n');
7366 }
7367
7368 // Read and store the number of holes if given
7369 // Skip line with commentary
7370 if (getline(poly_file, test_string, '#'))
7371 {
7372 poly_file.ignore(80, '\n');
7373
7374 unsigned dummy_hole;
7375 unsigned nhole;
7376 poly_file >> nhole;
7377
7378 triangulate_io.numberofholes = nhole;
7379 triangulate_io.holelist =
7380 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
7381
7382 // Loop over the holes to get centre coords and store value onto the
7383 // TriangulateIO object
7384 for (unsigned i = 0; i < 2 * nhole; i += 2)
7385 {
7386 poly_file >> dummy_hole;
7387 poly_file >> triangulate_io.holelist[i];
7388 poly_file >> triangulate_io.holelist[i + 1];
7389 }
7390 }
7391
7392 // Read and store the number of regions if given
7393 // Skip line with commentary
7394 if (getline(poly_file, test_string, '#'))
7395 {
7396 poly_file.ignore(80, '\n');
7397
7398 unsigned dummy_region;
7399 unsigned nregion;
7400 poly_file >> nregion;
7401 std::cerr << "Regions: " << nregion << std::endl;
7402 getchar();
7403
7404 triangulate_io.numberofregions = nregion;
7405 triangulate_io.regionlist =
7406 (double*)malloc(triangulate_io.numberofregions * 4 * sizeof(double));
7407
7408 // Check for using regions
7409 if (nregion > 0)
7410 {
7411 use_attributes = true;
7412 }
7413
7414 // Loop over the regions to get coords and store value onto the
7415 // TriangulateIO object
7416 for (unsigned i = 0; i < nregion; i++)
7417 {
7418 poly_file >> dummy_region;
7419 poly_file >> triangulate_io.regionlist[4 * i];
7420 poly_file >> triangulate_io.regionlist[4 * i + 1];
7421 poly_file >> triangulate_io.regionlist[4 * i + 2];
7422 triangulate_io.regionlist[4 * i + 3] = 0.0;
7423 }
7424 }
7425 }
7426
7427#endif
7428
7429#ifdef OOMPH_HAS_TRIANGLE_LIB
7430#ifdef OOMPH_HAS_MPI
7431
7432 //======================================================================
7433 /// Used to dump info. related with distributed triangle meshes
7434 //======================================================================
7435 template<class ELEMENT>
7437 std::ostream& dump_file)
7438 {
7439 // First check that the mesh is distributed
7440 if (this->is_mesh_distributed())
7441 {
7442 // Save the original number of boundaries
7443 const unsigned nboundary = this->nboundary();
7444 dump_file << nboundary << " # number of original boundaries" << std::endl;
7445
7446 // Save the number of shared boundaries
7447 const unsigned nshared_boundaries = this->nshared_boundaries();
7448 dump_file << nshared_boundaries << " # number of shared boundaries"
7449 << std::endl;
7450
7451 // Save the initial and final shared boundaries ids
7452 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
7453 dump_file << init_shd_bnd_id << " # initial shared boundaries id"
7454 << std::endl;
7455
7456 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
7457 dump_file << final_shd_bnd_id << " # final shared boundaries id"
7458 << std::endl;
7459
7460 // Save the number of processors
7461 const unsigned nprocs = this->shared_boundaries_ids().size();
7462 dump_file << nprocs << " # number of processors" << std::endl;
7463
7464 // Now save the processors ids and the shared boundary created
7465 // by them
7466 for (unsigned ip = 0; ip < nprocs; ip++)
7467 {
7468 for (unsigned jp = 0; jp < nprocs; jp++)
7469 {
7470 if (ip != jp)
7471 {
7472 // Get the number of shared boundaries with it these two
7473 // processors
7474 const unsigned nshared_boundaries_iproc_jproc =
7475 this->shared_boundaries_ids(ip, jp).size();
7476
7477 // Save the number of shared boundaries with in these two
7478 // processors
7479 dump_file << nshared_boundaries_iproc_jproc
7480 << " # number of shared boundaries with in two "
7481 << "processors" << std::endl;
7482 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7483 {
7484 const unsigned shared_boundary_id =
7485 this->shared_boundaries_ids(ip, jp, is);
7486 dump_file << ip << " " << jp << " " << shared_boundary_id
7487 << " # ip jp shared_boundary of processors ip and jp"
7488 << std::endl;
7489
7490 } // for (is < nshared_boundaries_iproc_jproc)
7491 }
7492 } // for (jp < nprocs)
7493 } // for (ip < nprocs)
7494
7495 // Now save the info. that states which shared boundary overlaps
7496 // an internal boundary
7497
7498 // First check if there are shared boundaries overlapping internal
7499 // boundaries
7500 const unsigned nshared_boundaries_overlap_internal_boundaries =
7501 this->nshared_boundary_overlaps_internal_boundary();
7502 dump_file << nshared_boundaries_overlap_internal_boundaries
7503 << " # number of shared boundaries that overlap internal "
7504 << "boundaries" << std::endl;
7505
7506 if (nshared_boundaries_overlap_internal_boundaries > 0)
7507 {
7508 for (unsigned isb = init_shd_bnd_id; isb < final_shd_bnd_id; isb++)
7509 {
7510 // Check if the current shared boundary overlaps an internal
7511 // boundary
7512 if (this->shared_boundary_overlaps_internal_boundary(isb))
7513 {
7514 // Which internal boundary is overlapped by the shared
7515 // boundary
7516 const unsigned overlapped_internal_boundary =
7517 shared_boundary_overlapping_internal_boundary(isb);
7518 // Save the shared boundary that overlaps the internal boundary
7519 dump_file << isb << " " << overlapped_internal_boundary
7520 << " # the shared boundary overlaps the internal "
7521 << "boundary " << std::endl;
7522
7523 } // if (this->shared_boundary_overlaps_internal_boundary(isb))
7524 } // for (isb < final_shd_bnd_id)
7525 } // if (nshared_boundaries_overlap_internal_boundaries > 0)
7526
7527 // Now save the info. related with the initial and final
7528 // boundary coordinates for each original boundary
7529
7530 // Go through all the (original) boundaries to update the initial
7531 // and final boundary coordinates
7532 for (unsigned b = 0; b < nboundary; b++)
7533 {
7534 // Check if the boundary zeta coordinates for this boundary have
7535 // been already assigned, if that is the case then state the
7536 // flag to know that info. should be read
7537 if (Assigned_segments_initial_zeta_values[b])
7538 {
7539 // The boundary coordinates have been computed then state
7540 // the flag and save the info.
7541 dump_file << "1 # assigned boundary coordinates initial zeta values"
7542 << std::endl;
7543
7544 // Save the initial and final boundary coordinates, same as
7545 // the initial and final zeta values for each boundary
7546
7547 // First the vertices coordinates
7548 Vector<double> initial_coordinates =
7549 this->boundary_initial_coordinate(b);
7550
7551 Vector<double> final_coordinates = this->boundary_final_coordinate(b);
7552
7553 dump_file << std::setprecision(14) << initial_coordinates[0] << " "
7554 << initial_coordinates[1]
7555 << " # initial coordinates for the current boundary"
7556 << std::endl;
7557
7558 dump_file << std::setprecision(14) << final_coordinates[0] << " "
7559 << final_coordinates[1]
7560 << " # final coordinates for the current boundary"
7561 << std::endl;
7562
7563 // ... then the zeta values
7564
7565#ifdef PARANOID
7566 // Get the number of zeta coordinates (should be one)
7567 const unsigned zeta_size =
7568 this->boundary_initial_zeta_coordinate(b).size();
7569
7570 if (zeta_size != 1)
7571 {
7572 std::ostringstream error_message;
7573 error_message
7574 << "The dimension for the zeta values container is different\n"
7575 << "from 1, the current implementation only supports\n"
7576 << "one-dimensioned zeta containers\n\n";
7577 throw OomphLibError(
7578 error_message.str(),
7579 "TriangleMesh::dump_distributed_info_for_restart()",
7580 OOMPH_EXCEPTION_LOCATION);
7581 }
7582#endif
7583
7584 Vector<double> zeta_initial =
7585 this->boundary_initial_zeta_coordinate(b);
7586 Vector<double> zeta_final = this->boundary_final_zeta_coordinate(b);
7587
7588 dump_file << std::setprecision(14) << zeta_initial[0]
7589 << " # initial zeta value for the current boundary"
7590 << std::endl;
7591
7592 dump_file << std::setprecision(14) << zeta_final[0]
7593 << " # final zeta value for the current boundary"
7594 << std::endl;
7595
7596 // Get the number of segments of the current boundary
7597 const unsigned nsegments = this->nboundary_segment(b);
7598 // Save the number of segments of the current boundary
7599 dump_file << b << " " << nsegments
7600 << " # of segments for the current boundary" << std::endl;
7601
7602 // ... and then save that info for each segments
7603 for (unsigned is = 0; is < nsegments; is++)
7604 {
7605 // First the vertices coordinates
7606 Vector<double> initial_segment_coordinates =
7607 this->boundary_segment_initial_coordinate(b)[is];
7608 Vector<double> final_segment_coordinates =
7609 this->boundary_segment_final_coordinate(b)[is];
7610
7611 dump_file
7612 << std::setprecision(14) << initial_segment_coordinates[0] << " "
7613 << initial_segment_coordinates[1]
7614 << " # initial segment coordinates for the current boundary"
7615 << std::endl;
7616
7617 dump_file << std::setprecision(14) << final_segment_coordinates[0]
7618 << " " << final_segment_coordinates[1]
7619 << " # final segment coordinates for the current boundary"
7620 << std::endl;
7621
7622 // ... then the zeta values
7623
7624 if (this->boundary_geom_object_pt(b) != 0)
7625 {
7626 const double zeta_segment_initial =
7627 this->boundary_segment_initial_zeta(b)[is];
7628 const double zeta_segment_final =
7629 this->boundary_segment_final_zeta(b)[is];
7630
7631 dump_file
7632 << std::setprecision(14) << zeta_segment_initial
7633 << " # initial segment zeta value for the current boundary"
7634 << std::endl;
7635
7636 dump_file
7637 << std::setprecision(14) << zeta_segment_final
7638 << " # final segment zeta value for the current boundary"
7639 << std::endl;
7640 }
7641 else
7642 {
7643 const double arclength_segment_initial =
7644 this->boundary_segment_initial_arclength(b)[is];
7645 const double arclength_segment_final =
7646 this->boundary_segment_final_arclength(b)[is];
7647
7648 dump_file
7649 << std::setprecision(14) << arclength_segment_initial
7650 << " # initial segment arclength for the current boundary"
7651 << std::endl;
7652
7653 dump_file << std::setprecision(14) << arclength_segment_final
7654 << " # final segment arclength for the current boundary"
7655 << std::endl;
7656
7657 } // else if (this->boundary_geom_object_pt(b)!=0)
7658
7659 } // for (is < nsegments)
7660
7661 } // if (Assigned_segments_initial_zeta_values[b])
7662 else
7663 {
7664 // The boundary coordinates have NOT been computed then state
7665 // the flag and save the info.
7666 dump_file << "0 # assigned boundary coordinates initial zeta values"
7667 << std::endl;
7668 }
7669
7670 } // for (b < nboundary)
7671
7672 } // if (this->is_mesh_distributed())
7673 }
7674
7675 //======================================================================
7676 /// Used to read info. related with distributed triangle meshes
7677 //======================================================================
7678 template<class ELEMENT>
7680 std::istream& restart_file)
7681 {
7682 // First check that the mesh is distributed
7683 if (this->is_mesh_distributed())
7684 {
7685 // Read the number of original boundaries
7686 const unsigned n_boundary = read_unsigned_line_helper(restart_file);
7687
7688#ifdef PARANOID
7689 if (n_boundary != this->nboundary())
7690 {
7691 std::ostringstream error_message;
7692 error_message
7693 << "The number of boundaries (" << n_boundary << ") on the "
7694 << "file used for restarting is different\nfrom the number of "
7695 << "boundaries (" << this->nboundary() << ") on the current "
7696 << "mesh!!!\n\n\n";
7697 throw OomphLibError(error_message.str(),
7698 "TriangleMesh::read_distributed_info_for_restart()",
7699 OOMPH_EXCEPTION_LOCATION);
7700 }
7701#endif
7702
7703 // Read the number of shared boundaries
7704 unsigned n_shared_boundaries = read_unsigned_line_helper(restart_file);
7705 // We need to read the data because it comes in the file (add and
7706 // substract to avoid compilation warning)
7707 n_shared_boundaries++;
7708 n_shared_boundaries--;
7709
7710 // Read the initial and final shared boundaries ids
7711 unsigned init_shd_bnd_id = read_unsigned_line_helper(restart_file);
7712 // We need to read the data because it comes in the file (add and
7713 // substract to avoid compilation warning)
7714 init_shd_bnd_id++;
7715 init_shd_bnd_id--;
7716 // Add and substract to avoid compilation warning
7717 unsigned final_shd_bnd_id = read_unsigned_line_helper(restart_file);
7718 // We need to read the data because it comes in the file (add and
7719 // substract to avoid compilation warning)
7720 final_shd_bnd_id++;
7721 final_shd_bnd_id--;
7722
7723 // Read the number of processors involved in the generation of
7724 // mesh before restart
7725 const unsigned n_procs = read_unsigned_line_helper(restart_file);
7726
7727#ifdef PARANOID
7728 if (static_cast<int>(n_procs) != this->communicator_pt()->nproc())
7729 {
7730 std::ostringstream error_message;
7731 error_message
7732 << "The number of previously used processors (" << n_procs
7733 << ") (read from the restart file) is different\nfrom the "
7734 << "number of current used processors ("
7735 << this->communicator_pt()->nproc() << ")\n\n";
7736 throw OomphLibError(error_message.str(),
7737 "TriangleMesh::read_distributed_info_for_restart()",
7738 OOMPH_EXCEPTION_LOCATION);
7739 }
7740#endif
7741
7742 // Clear all previuos info. related with shared boundaries
7743 this->shared_boundaries_ids().clear();
7744 this->shared_boundary_from_processors().clear();
7745 this->shared_boundary_overlaps_internal_boundary().clear();
7746
7747 // Create the storage for the shared boundaries ids related with
7748 // the processors
7749 this->shared_boundaries_ids().resize(n_procs);
7750
7751 // Now read the processors ids and the shared boundary created
7752 // by them
7753 for (unsigned ip = 0; ip < n_procs; ip++)
7754 {
7755 // Create the storage for the shared boundaries ids related with
7756 // the processors
7757 this->shared_boundaries_ids(ip).resize(n_procs);
7758 for (unsigned jp = 0; jp < n_procs; jp++)
7759 {
7760 if (ip != jp)
7761 {
7762 // Read the number of shared boundaries with in these two
7763 // processors
7764 const unsigned nshared_boundaries_iproc_jproc =
7765 read_unsigned_line_helper(restart_file);
7766 for (unsigned is = 0; is < nshared_boundaries_iproc_jproc; is++)
7767 {
7768 // Get the processors
7769 unsigned tmp_ip;
7770 restart_file >> tmp_ip;
7771 unsigned tmp_jp;
7772 restart_file >> tmp_jp;
7773
7774 // Get the shared boundary id created by these two
7775 // processors
7776 const unsigned shared_boundary_id =
7777 read_unsigned_line_helper(restart_file);
7778
7779 // Update the info. of the processors that give rise to
7780 // the shared boundaries
7781 this->shared_boundaries_ids(ip, jp).push_back(shared_boundary_id);
7782
7783 // Update the structure that states the processors that
7784 // gave rise to the shared boundary
7785 Vector<unsigned> processors(2);
7786 processors[0] = ip;
7787 processors[1] = jp;
7788 this->shared_boundary_from_processors()[shared_boundary_id] =
7789 processors;
7790
7791 } // for (is < nshared_boundaries_iproc_jproc)
7792 }
7793 } // for (jp < n_procs)
7794 } // for (ip < n_procs)
7795
7796 // Now read the info. that states which shared boundary overlaps
7797 // an internal boundary
7798
7799 // First check if there are shared boundaries overlapping internal
7800 // boundaries
7801 const unsigned nshared_boundaries_overlap_internal_boundaries =
7802 read_unsigned_line_helper(restart_file);
7803
7804 for (unsigned isb = 0;
7805 isb < nshared_boundaries_overlap_internal_boundaries;
7806 isb++)
7807 {
7808 // Read the shared boundary that overlaps an internal boundary
7809 unsigned shared_boundary_overlapping;
7810 restart_file >> shared_boundary_overlapping;
7811 // ... and read the internal boundary that overlaps
7812 const unsigned overlapped_internal_boundary =
7813 read_unsigned_line_helper(restart_file);
7814
7815 // Re-establish the info. of the shared boundaries overlapped
7816 // by internal boundaries
7817 this->shared_boundary_overlaps_internal_boundary()
7818 [shared_boundary_overlapping] = overlapped_internal_boundary;
7819 } // for (isb < nshared_boundaries_overlap_internal_boundaries)
7820
7821 // Now read the info. related with the initial and final
7822 // boundary coordinates for each original boundary
7823
7824 // Go through all the (original) boundaries to update the initial
7825 // and final boundary coordinates
7826 for (unsigned b = 0; b < n_boundary; b++)
7827 {
7828 // For each boundary check if the boundary coordinates initial
7829 // and final zeta vales were assigned in the restart file
7830 const unsigned boundary_coordinates_initial_zeta_values_assigned =
7831 read_unsigned_line_helper(restart_file);
7832
7833 if (boundary_coordinates_initial_zeta_values_assigned)
7834 {
7835 // Clear any previous stored info. There should not be
7836 // info. already stored but better clear the info. for the
7837 // boundary
7838 Boundary_initial_coordinate[b].clear();
7839 Boundary_final_coordinate[b].clear();
7840
7841 Boundary_initial_zeta_coordinate[b].clear();
7842 Boundary_final_zeta_coordinate[b].clear();
7843
7844 // The info. for the segments
7845 Boundary_segment_inverted[b].clear();
7846 Boundary_segment_initial_coordinate[b].clear();
7847 Boundary_segment_final_coordinate[b].clear();
7848
7849 Boundary_segment_initial_zeta[b].clear();
7850 Boundary_segment_final_zeta[b].clear();
7851
7852 Boundary_segment_initial_arclength[b].clear();
7853 Boundary_segment_final_arclength[b].clear();
7854
7855 // Read the initial and final boundary coordinates, same as
7856 // the initial and final zeta values for each boundary
7857
7858 // First the vertices coordinates
7859 Vector<double> initial_coordinates(2);
7860
7861 // Read the initial coordinates
7862 restart_file >> initial_coordinates[0] >> initial_coordinates[1];
7863
7864 // Ignore rest of line
7865 restart_file.ignore(80, '\n');
7866
7867 Vector<double> final_coordinates(2);
7868
7869 // Read the final coordinates
7870 restart_file >> final_coordinates[0] >> final_coordinates[1];
7871
7872 // Ignore rest of line
7873 restart_file.ignore(80, '\n');
7874
7875 // Set the values in the containers
7876
7877
7878 this->boundary_initial_coordinate(b) = initial_coordinates;
7879 this->boundary_final_coordinate(b) = final_coordinates;
7880
7881 // ... now read the zeta values
7882 Vector<double> zeta_initial(1);
7883 restart_file >> zeta_initial[0];
7884
7885 // Ignore rest of line
7886 restart_file.ignore(80, '\n');
7887
7888 Vector<double> zeta_final(1);
7889 restart_file >> zeta_final[0];
7890
7891 // Ignore rest of line
7892 restart_file.ignore(80, '\n');
7893
7894 // Set the values in the containers
7895 this->boundary_initial_zeta_coordinate(b) = zeta_initial;
7896 this->boundary_final_zeta_coordinate(b) = zeta_final;
7897
7898 // Get the curent boundary id from the restart file
7899 unsigned current_boundary;
7900 restart_file >> current_boundary;
7901
7902#ifdef PARANOID
7903 if (current_boundary != b)
7904 {
7905 std::ostringstream error_message;
7906 error_message
7907 << "The current boundary id from the restart file ("
7908 << current_boundary << ") is different from\nthe boundary id "
7909 << b << "currently used to re-establish the initial and\nfinal "
7910 << "segment's zeta values\n\n";
7911 throw OomphLibError(
7912 error_message.str(),
7913 "TriangleMesh::read_distributed_info_for_restart()",
7914 OOMPH_EXCEPTION_LOCATION);
7915 }
7916#endif
7917
7918 // ... and its number of segments
7919 unsigned nsegments;
7920 restart_file >> nsegments;
7921
7922 // Ignore rest of line
7923 restart_file.ignore(80, '\n');
7924
7925 // Now read all the segments info.
7926
7927 // ... and then save that info for each segments
7928 for (unsigned is = 0; is < nsegments; is++)
7929 {
7930 // First the vertices coordinates
7931 Vector<double> initial_segment_coordinates(2);
7932
7933 // Read the initial coordinates
7934 restart_file >> initial_segment_coordinates[0] >>
7935 initial_segment_coordinates[1];
7936
7937 // Ignore rest of line
7938 restart_file.ignore(80, '\n');
7939
7940 Vector<double> final_segment_coordinates(2);
7941
7942 // Read the final coordinates
7943 restart_file >> final_segment_coordinates[0] >>
7944 final_segment_coordinates[1];
7945
7946 // Ignore rest of line
7947 restart_file.ignore(80, '\n');
7948
7949 // Set the values in the containers
7950 this->boundary_segment_initial_coordinate(b).push_back(
7951 initial_segment_coordinates);
7952 this->boundary_segment_final_coordinate(b).push_back(
7953 final_segment_coordinates);
7954
7955 // ... then the zeta values for the segment
7956 if (this->boundary_geom_object_pt(b) != 0)
7957 {
7958 Vector<double> zeta_segment_initial(1);
7959 restart_file >> zeta_segment_initial[0];
7960
7961 // Ignore rest of line
7962 restart_file.ignore(80, '\n');
7963
7964 Vector<double> zeta_segment_final(1);
7965 restart_file >> zeta_segment_final[0];
7966
7967 // Ignore rest of line
7968 restart_file.ignore(80, '\n');
7969
7970 // Set the values in the containers for the segment
7971 this->boundary_segment_initial_zeta(b).push_back(
7972 zeta_segment_initial[0]);
7973 this->boundary_segment_final_zeta(b).push_back(
7974 zeta_segment_final[0]);
7975 }
7976 else
7977 {
7978 Vector<double> arclength_segment_initial(1);
7979 restart_file >> arclength_segment_initial[0];
7980
7981 // Ignore rest of line
7982 restart_file.ignore(80, '\n');
7983
7984 Vector<double> arclength_segment_final(1);
7985 restart_file >> arclength_segment_final[0];
7986
7987 // Ignore rest of line
7988 restart_file.ignore(80, '\n');
7989
7990 // Set the values in the containers for the segment
7991 this->boundary_segment_initial_arclength(b).push_back(
7992 arclength_segment_initial[0]);
7993 this->boundary_segment_final_arclength(b).push_back(
7994 arclength_segment_final[0]);
7995 } // else if (this->boundary_geom_object_pt(b)!=0)
7996
7997 } // for (is < nsegments)
7998
7999 } // if (boundary_coordinates_initial_zeta_values_assigned)
8000
8001 } // for (b < n_boundary)
8002
8003 } // if (this->is_mesh_distributed())
8004 }
8005
8006#endif // #ifdef OOMPH_HAS_MPI
8007#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
8008
8009 //===================================================================
8010 // Output the nodes on the boundaries and their / respective boundary
8011 // coordinates(into separate tecplot / zones)
8012 //===================================================================
8013 template<class ELEMENT>
8015 std::ostream& outfile)
8016 {
8017 // First get all the elements adjacent to the given boundary, then
8018 // the face elements and extract the nodes on the boundaries using
8019 // the face elements. We can not use the data structure
8020 // Boundary_node_pt since the multi_domain functions add nodes there
8021 // without assigning the required boundary coordinate
8022
8023 // Store the nodes in a set so we do not have repeated nodes
8024 std::set<Node*> boundary_nodes_pt;
8025 const unsigned n_boundary_ele = this->nboundary_element(b);
8026 for (unsigned e = 0; e < n_boundary_ele; e++)
8027 {
8028 // Get the boundary bulk element
8029 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
8030#ifdef OOMPH_HAS_MPI
8031 // Only work with nonhalo elements if the mesh is distributed
8032 if (!bulk_ele_pt->is_halo())
8033 {
8034#endif
8035 // Get the face index
8036 int face_index = this->face_index_at_boundary(b, e);
8037 // Create the face element
8038 FiniteElement* face_ele_pt =
8039 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
8040
8041 // Get the number of nodes on the face element
8042 const unsigned n_nodes = face_ele_pt->nnode();
8043 for (unsigned i = 0; i < n_nodes; i++)
8044 {
8045 // Get the nodes in the face elements
8046 Node* tmp_node_pt = face_ele_pt->node_pt(i);
8047 // Add the nodes to the set of boundary nodes
8048 boundary_nodes_pt.insert(tmp_node_pt);
8049 } // for (i < n_nodes)
8050
8051 // Free the memory allocated for the face element
8052 delete face_ele_pt;
8053 face_ele_pt = 0;
8054#ifdef OOMPH_HAS_MPI
8055 } // if (!bulk_ele_pt->is_halo())
8056#endif
8057
8058 } // for (e < n_boundary_ele)
8059
8060 outfile << "ZONE T=\"Boundary nodes" << b << "\"\n";
8061 // Set to store the boundary nodes in order
8062 std::set<Vector<double>> set_node_coord;
8063 // Loop over the nodes on the boundary and store them in the set
8064 for (std::set<Node*>::iterator it = boundary_nodes_pt.begin();
8065 it != boundary_nodes_pt.end();
8066 it++)
8067 {
8068 Node* inode_pt = (*it);
8069
8070 // Get the node coordinates
8071 const unsigned n_dim = inode_pt->ndim();
8072 Vector<double> node_coord(n_dim + 1);
8073
8074 // Get the boundary coordinate
8075 Vector<double> zeta(1);
8076 inode_pt->get_coordinates_on_boundary(b, zeta);
8077 node_coord[0] = zeta[0];
8078 for (unsigned j = 0; j < n_dim; j++)
8079 {
8080 node_coord[j + 1] = inode_pt->x(j);
8081 }
8082 set_node_coord.insert(node_coord);
8083 }
8084
8085 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8086 it != set_node_coord.end();
8087 it++)
8088 {
8089 // Get the node coordinates
8090 Vector<double> node_coord = (*it);
8091
8092 // Output the node coordinates
8093 const unsigned n_dim = node_coord.size() - 1;
8094 for (unsigned j = 0; j < n_dim; j++)
8095 {
8096 outfile << node_coord[j + 1] << " ";
8097 }
8098 // ... add an extra coordinate to avoid error with tecplot
8099 outfile << "0.0" << std::endl;
8100 }
8101
8102 // ... loop again to plot the bound coordinates
8103 outfile << "ZONE T=\"Boundary coordinates " << b << "\"\n";
8104 for (std::set<Vector<double>>::iterator it = set_node_coord.begin();
8105 it != set_node_coord.end();
8106 it++)
8107 {
8108 // Get the node coordinates
8109 Vector<double> node_coord = (*it);
8110
8111 // Output the node coordinates
8112 const unsigned n_dim = node_coord.size() - 1;
8113 for (unsigned j = 0; j < n_dim; j++)
8114 {
8115 outfile << node_coord[j + 1] << " ";
8116 }
8117
8118 // Output the boundary coordinate
8119 outfile << node_coord[0] << std::endl;
8120 }
8121 }
8122
8123#ifdef OOMPH_HAS_MPI
8124 //====================================================================
8125 // Creates the distributed domain representation. Joins the
8126 // original boundaires, shared boundaries and creates connections among
8127 // them to create the new polygons that represent the distributed
8128 // domain
8129 //====================================================================
8130 template<class ELEMENT>
8132 Vector<TriangleMeshPolygon*>& polygons_pt,
8133 Vector<TriangleMeshOpenCurve*>& open_curves_pt)
8134 {
8135 // Get the outer polygons, internal polygons, internal open curves
8136 // and join them with the shared polylines to create the distributed
8137 // domain representation (the new outer, internal polygons, and new
8138 // internal open curves)
8139
8140 // Get the rank of the current processor
8141 const unsigned my_rank = this->communicator_pt()->my_rank();
8142
8143 // *********************************************************************
8144 // Step (2) Get the outer, internal and shared boundaries to create the
8145 // new polygons
8146 // *********************************************************************
8147
8148 // *********************************************************************
8149 // Step (2.1) Get the outer boundaries and check if it is necessary to use
8150 // a new representation (for example when the boundary was splitted in
8151 // the distribution process)
8152 // *********************************************************************
8153
8154 // Storage for new created polylines, non sorted
8155 Vector<TriangleMeshPolyLine*> unsorted_outer_polyline_pt;
8156
8157 // Storing for the polylines on the boundaries
8158 // The first index is for a set of connected polylines
8159 // The second index is for a polyline on a set of connected polylines
8160 Vector<Vector<TriangleMeshPolyLine*>> sorted_outer_curves_pt;
8161
8162 // Copy the outer boundaries to the vector of polylines
8163 const unsigned nouter = this->Outer_boundary_pt.size();
8164 for (unsigned i = 0; i < nouter; i++)
8165 {
8166 const unsigned npolylines = this->Outer_boundary_pt[i]->npolyline();
8167 for (unsigned p = 0; p < npolylines; p++)
8168 {
8169 // Pointer to the current polyline
8170 TriangleMeshPolyLine* tmp_polyline_pt =
8171 this->Outer_boundary_pt[i]->polyline_pt(p);
8172 const unsigned nvertex = tmp_polyline_pt->nvertex();
8173 if (nvertex > 0)
8174 {
8175 // Get the boundary id of the polyline and check if that boundary
8176 // needs a new representation (for example when the boundary was
8177 // splitted in the distribution process)
8178 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8179 if (!boundary_was_splitted(bound_id))
8180 {
8181 unsorted_outer_polyline_pt.push_back(tmp_polyline_pt);
8182 } // if (!boundary_was_splitted(bound_id))
8183 else
8184 {
8185 // Get the polylines that will represent this boundary
8186 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8187 boundary_subpolylines(bound_id);
8188 const unsigned nsub_poly = tmp_vector_polylines.size();
8189#ifdef PARANOID
8190 if (nsub_poly <= 1)
8191 {
8192 std::ostringstream error_message;
8193 error_message << "The boundary (" << bound_id
8194 << ") was marked to be splitted but\n"
8195 << "there are only (" << nsub_poly
8196 << ") polylines to represent it.\n";
8197 throw OomphLibError(error_message.str(),
8198 OOMPH_CURRENT_FUNCTION,
8199 OOMPH_EXCEPTION_LOCATION);
8200 }
8201#endif
8202 // Add the new representation of the polylines (sub-polylines)
8203 // to represent this boundary
8204 for (unsigned isub = 0; isub < nsub_poly; isub++)
8205 {
8206 unsorted_outer_polyline_pt.push_back(tmp_vector_polylines[isub]);
8207#ifdef PARANOID
8208 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8209 if (nsvertex == 0)
8210 {
8211 std::ostringstream error_message;
8212 error_message
8213 << "The current chunk (" << isub << ") of the polyline with\n"
8214 << "boundary id (" << bound_id << ") has no vertices\n";
8215 throw OomphLibError(error_message.str(),
8216 OOMPH_CURRENT_FUNCTION,
8217 OOMPH_EXCEPTION_LOCATION);
8218 } // if (nsvertex == 0)
8219#endif // #ifdef PARANOID
8220 } // for (isub < nsub_poly)
8221 } // else if (!boundary_was_splitted(bound_id))
8222 } // if (nvertex > 0)
8223 } // for (p < npolylines)
8224 } // for (i < nouter)
8225
8226 // Get the number of unsorted polylines
8227 unsigned nunsorted_outer_polyline = unsorted_outer_polyline_pt.size();
8228 if (nunsorted_outer_polyline > 0)
8229 {
8230 // Now that we have all the new unsorted polylines it is time to sort them
8231 // so they be all contiguous
8232 sort_polylines_helper(unsorted_outer_polyline_pt, sorted_outer_curves_pt);
8233
8234 } // if (nunsorted_outer_polyline > 0)
8235
8236 // *********************************************************************
8237 // Step (2.2) Get the internal closed boundaries and check if it is
8238 // necessary to use a new representation (for example when the boundary
8239 // was splitted in the distribution process)
8240 // *********************************************************************
8241
8242 // Storage for new created polylines, non sorted
8243 Vector<TriangleMeshPolyLine*> unsorted_internal_closed_polyline_pt;
8244
8245 // Storing for the polylines on the boundaries
8246 // The first index is for a set of connected polylines
8247 // The second index is for a polyline on a set of connected polylines
8248 Vector<Vector<TriangleMeshPolyLine*>> sorted_internal_closed_curves_pt;
8249
8250 // Copy the internal closed boundaries to the vector of polylines
8251 const unsigned ninternal_closed = this->Internal_polygon_pt.size();
8252 for (unsigned i = 0; i < ninternal_closed; i++)
8253 {
8254 const unsigned npolylines = this->Internal_polygon_pt[i]->npolyline();
8255 for (unsigned p = 0; p < npolylines; p++)
8256 {
8257 // Pointer to the current polyline
8258 TriangleMeshPolyLine* tmp_polyline_pt =
8259 this->Internal_polygon_pt[i]->polyline_pt(p);
8260 const unsigned nvertex = tmp_polyline_pt->nvertex();
8261 if (nvertex > 0)
8262 {
8263 // Get the boundary id of the polyline and check if that boundary
8264 // needs a new representation (for example when the boundary was
8265 // splitted in the distribution process)
8266 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8267 if (!boundary_was_splitted(bound_id))
8268 {
8269 unsorted_internal_closed_polyline_pt.push_back(tmp_polyline_pt);
8270 } // if (!boundary_was_splitted(bound_id))
8271 else
8272 {
8273 // Get the polylines that will represent this boundary
8274 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8275 boundary_subpolylines(bound_id);
8276 const unsigned nsub_poly = tmp_vector_polylines.size();
8277#ifdef PARANOID
8278 if (nsub_poly <= 1)
8279 {
8280 std::ostringstream error_message;
8281 error_message << "The boundary (" << bound_id
8282 << ") was marked to be splitted but\n"
8283 << "there are only (" << nsub_poly
8284 << ") polylines to represent it.\n";
8285 throw OomphLibError(error_message.str(),
8286 OOMPH_CURRENT_FUNCTION,
8287 OOMPH_EXCEPTION_LOCATION);
8288 }
8289#endif
8290 // Add the new representation of the polylines (sub-polylines)
8291 // to represent this boundary
8292 for (unsigned isub = 0; isub < nsub_poly; isub++)
8293 {
8294 unsorted_internal_closed_polyline_pt.push_back(
8295 tmp_vector_polylines[isub]);
8296#ifdef PARANOID
8297 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8298 if (nsvertex == 0)
8299 {
8300 std::ostringstream error_message;
8301 error_message
8302 << "The current chunk (" << isub << ") of the polyline with\n"
8303 << "boundary id (" << bound_id << ") has no vertices\n";
8304 throw OomphLibError(error_message.str(),
8305 OOMPH_CURRENT_FUNCTION,
8306 OOMPH_EXCEPTION_LOCATION);
8307 } // if (nsvertex == 0)
8308#endif // #ifdef PARANOID
8309 } // for (isub < nsub_poly)
8310 } // else if (!boundary_was_splitted(bound_id))
8311 } // if (nvertex > 0)
8312 } // for (p < npolylines)
8313 } // for (i < ninternal_closed)
8314
8315 const unsigned nunsorted_internal_closed_polyline =
8316 unsorted_internal_closed_polyline_pt.size();
8317
8318 if (nunsorted_internal_closed_polyline > 0)
8319 {
8320 // Now that we have all the new unsorted polylines it is time to sort them
8321 // so they be all contiguous
8322 sort_polylines_helper(unsorted_internal_closed_polyline_pt,
8323 sorted_internal_closed_curves_pt);
8324 }
8325
8326 // *********************************************************************
8327 // Step (2.3) Get the internal open boundaries and check if it is
8328 // necessary to use a new representation (for example when the boundary
8329 // was splitted in the distribution process)
8330 // *********************************************************************
8331
8332 // Storage for new created polylines, non sorted
8333 Vector<TriangleMeshPolyLine*> unsorted_internal_open_polyline_pt;
8334
8335 // Storing for the polylines on the boundaries
8336 // The first index is for a set of connected polylines
8337 // The second index is for a polyline on a set of connected polylines
8338 Vector<Vector<TriangleMeshPolyLine*>> sorted_internal_open_curves_pt;
8339
8340 // Copy the internal open boundaries to the vector of polylines
8341 const unsigned ninternal_open = this->Internal_open_curve_pt.size();
8342 for (unsigned i = 0; i < ninternal_open; i++)
8343 {
8344 const unsigned ncurve_section =
8345 this->Internal_open_curve_pt[i]->ncurve_section();
8346 for (unsigned p = 0; p < ncurve_section; p++)
8347 {
8348 // Pointer to the current polyline
8349 TriangleMeshPolyLine* tmp_polyline_pt =
8350 this->Internal_open_curve_pt[i]->polyline_pt(p);
8351 const unsigned nvertex = tmp_polyline_pt->nvertex();
8352 if (nvertex > 0)
8353 {
8354 // Get the boundary id of the polyline and check if that boundary
8355 // needs a new representation (for example when the boundary was
8356 // splitted in the distribution process)
8357 const unsigned bound_id = tmp_polyline_pt->boundary_id();
8358 if (!boundary_was_splitted(bound_id))
8359 {
8360 // Only include as internal boundaries those not marked as
8361 // shared boundaries
8362 if (!boundary_marked_as_shared_boundary(bound_id, 0))
8363 {
8364 unsorted_internal_open_polyline_pt.push_back(tmp_polyline_pt);
8365 }
8366 } // if (!boundary_was_splitted(bound_id))
8367 else
8368 {
8369 // Get the polylines that will represent this boundary
8370 Vector<TriangleMeshPolyLine*> tmp_vector_polylines =
8371 boundary_subpolylines(bound_id);
8372 const unsigned nsub_poly = tmp_vector_polylines.size();
8373#ifdef PARANOID
8374 if (nsub_poly <= 1)
8375 {
8376 std::ostringstream error_message;
8377 error_message << "The boundary (" << bound_id
8378 << ") was marked to be splitted but\n"
8379 << "there are only (" << nsub_poly
8380 << ") polylines to represent it.\n";
8381 throw OomphLibError(error_message.str(),
8382 OOMPH_CURRENT_FUNCTION,
8383 OOMPH_EXCEPTION_LOCATION);
8384 }
8385#endif
8386 // Add the new representation of the polylines (sub-polylines)
8387 // to represent this boundary
8388 for (unsigned isub = 0; isub < nsub_poly; isub++)
8389 {
8390 // Only include as internal boundaries those not marked as
8391 // shared boundaries
8392 if (!boundary_marked_as_shared_boundary(bound_id, isub))
8393 {
8394 unsorted_internal_open_polyline_pt.push_back(
8395 tmp_vector_polylines[isub]);
8396 }
8397#ifdef PARANOID
8398 const unsigned nsvertex = tmp_vector_polylines[isub]->nvertex();
8399 if (nsvertex == 0)
8400 {
8401 std::ostringstream error_message;
8402 error_message
8403 << "The current chunk (" << isub << ") of the polyline with\n"
8404 << "boundary id (" << bound_id << ") has no vertices\n";
8405 throw OomphLibError(error_message.str(),
8406 OOMPH_CURRENT_FUNCTION,
8407 OOMPH_EXCEPTION_LOCATION);
8408 } // if (nsvertex == 0)
8409#endif // #ifdef PARANOID
8410 } // for (isub < nsub_poly)
8411 } // else if (!boundary_was_splitted(bound_id))
8412 } // if (nvertex > 0)
8413 } // for (p < npolylines)
8414 } // for (i < ninternal_open)
8415
8416 const unsigned nunsorted_internal_open_polyline =
8417 unsorted_internal_open_polyline_pt.size();
8418
8419 if (nunsorted_internal_open_polyline > 0)
8420 {
8421 // Now that we have all the new unsorted polylines it is time to sort them
8422 // so they be all contiguous
8423 sort_polylines_helper(unsorted_internal_open_polyline_pt,
8424 sorted_internal_open_curves_pt);
8425 }
8426
8427 // ********************************************************************
8428 // Step (2.4) Sort the polylines on the shared boundaries
8429 // ********************************************************************
8430
8431 // Storage for new created polylines, non sorted
8432 Vector<TriangleMeshPolyLine*> unsorted_shared_polyline_pt;
8433
8434 // Special storage for the shared polylines that will be also used
8435 // to connect with the internal boundaries
8436 Vector<TriangleMeshPolyLine*> unsorted_shared_to_internal_polyline_pt;
8437
8438 // Storing for the polylines on the shared boundaries
8439 // The first index is for a set of connected polylines
8440 // The second index is for a polyline on a set of connected polylines
8441 Vector<Vector<TriangleMeshPolyLine*>> sorted_shared_curves_pt;
8442
8443 // Copy the shared boudaries to the vector of polylines
8444 const unsigned ncurves = nshared_boundary_curves(my_rank);
8445 for (unsigned i = 0; i < ncurves; i++)
8446 {
8447 const unsigned npolylines = nshared_boundary_polyline(my_rank, i);
8448 for (unsigned p = 0; p < npolylines; p++)
8449 {
8450 const unsigned nvertex =
8451 shared_boundary_polyline_pt(my_rank, i, p)->nvertex();
8452 if (nvertex > 0)
8453 {
8454 TriangleMeshPolyLine* tmp_shared_poly_pt =
8455 shared_boundary_polyline_pt(my_rank, i, p);
8456
8457 // First check if there are shared boundaries overlapping
8458 // internal boundaries
8459 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
8460 {
8461 // Get the boundary id of the shared polyline
8462 const unsigned shd_bnd_id = tmp_shared_poly_pt->boundary_id();
8463 // If the shared polyline is marked as internal boundary
8464 // then include it in the special storage to look for
8465 // connection with internal boundaries
8466 if (this->shared_boundary_overlaps_internal_boundary(shd_bnd_id))
8467 {
8468 unsorted_shared_to_internal_polyline_pt.push_back(
8469 tmp_shared_poly_pt);
8470 }
8471 }
8472 unsorted_shared_polyline_pt.push_back(tmp_shared_poly_pt);
8473 }
8474 }
8475 }
8476
8477 // Get the total number of shared polylines
8478 const unsigned nunsorted_shared_polyline =
8479 unsorted_shared_polyline_pt.size();
8480
8481 if (nunsorted_shared_polyline > 0)
8482 {
8483 // Now that we have all the new unsorted polylines it is time to
8484 // sort them so they be all contiguous
8485 sort_polylines_helper(unsorted_shared_polyline_pt,
8486 sorted_shared_curves_pt);
8487 }
8488
8489 // ********************************************************************
8490 // Step (3) Join the boundaries (shared, internal and outer to
8491 // create the new polygons)
8492 // ********************************************************************
8493
8494 // Create the set of curves that will be used to create the new polygons
8495 // Get the total number of curves
8496 const unsigned nouter_curves = sorted_outer_curves_pt.size();
8497 const unsigned ninternal_closed_curves =
8498 sorted_internal_closed_curves_pt.size();
8499 const unsigned nshared_curves = sorted_shared_curves_pt.size();
8500 const unsigned ntotal_curves =
8501 nouter_curves + ninternal_closed_curves + nshared_curves;
8502
8503 // Add all the polylines to a container
8504 unsigned counter = 0;
8505 Vector<Vector<TriangleMeshPolyLine*>> all_curves_pt(ntotal_curves);
8506
8507 // Add the shared curves first, this ensure the generation of
8508 // internal polygons defined by the shared boundaries
8509 for (unsigned i = 0; i < nshared_curves; i++, counter++)
8510 {
8511 all_curves_pt[counter] = sorted_shared_curves_pt[i];
8512 }
8513
8514 // Add the internal polygons (if any)
8515 for (unsigned i = 0; i < ninternal_closed_curves; i++, counter++)
8516 {
8517 all_curves_pt[counter] = sorted_internal_closed_curves_pt[i];
8518 }
8519
8520 // Add the outer polygons
8521 for (unsigned i = 0; i < nouter_curves; i++, counter++)
8522 {
8523 all_curves_pt[counter] = sorted_outer_curves_pt[i];
8524 }
8525
8526 // Create the temporary version of the domain by joining the new
8527 // polylines
8528 this->create_tmp_polygons_helper(all_curves_pt, polygons_pt);
8529 // Create the new open curves
8530 this->create_tmp_open_curves_helper(sorted_internal_open_curves_pt,
8531 unsorted_shared_to_internal_polyline_pt,
8532 open_curves_pt);
8533
8534 // ********************************************************************
8535 // Step (4) Create connections among the outer boundaries
8536 // (intersections with themselves)
8537 // ********************************************************************
8538
8539 // After creating the new boundaries representation (polylines)
8540 // establish the connections of the shared boundaries (with
8541 // themselves or with the original boundaries). This avoids the
8542 // multiple definition of vertices in the domain which cause
8543 // problems when calling Triangle
8544
8545 this->create_shared_polylines_connections();
8546
8547 // ------------------------------------------------------------------
8548 // Compute the new holes information. Those from the
8549 // extra_holes_coordinates container, and those from the original
8550 // closed boundaries. Add the holes created by the halo elements
8551 // adjacent to the shared boundaries
8552
8553 // The storage for the new holes, get those from the
8554 // extra_holes_coordinates container and those from the internal
8555 // closed boundaries that are defined as holes
8556 Vector<Vector<double>> new_holes_coordinates;
8557
8558 // Copy the holes (those defined by the original internal closed
8559 // boundaries and those in the extra holes container)
8560
8561 // The holes defined by the original internal closed boundaries
8562 const unsigned n_holes = this->Internal_polygon_pt.size();
8563 for (unsigned h = 0; h < n_holes; h++)
8564 {
8565 Vector<double> hole_coordinates =
8566 this->Internal_polygon_pt[h]->internal_point();
8567 // If the closed boundary is a hole, then copy its hole
8568 if (!hole_coordinates.empty())
8569 {
8570 new_holes_coordinates.push_back(hole_coordinates);
8571 }
8572 } // for (h < n_holes)
8573
8574 // Is this the first time we are going to copy the extra holes
8575 // coordinates
8576 if (First_time_compute_holes_left_by_halo_elements)
8577 {
8578 // The holes in the extra holes container
8579 const unsigned n_extra_holes = Extra_holes_coordinates.size();
8580 for (unsigned h = 0; h < n_extra_holes; h++)
8581 {
8582 Vector<double> hole_coordinates = Extra_holes_coordinates[h];
8583 new_holes_coordinates.push_back(hole_coordinates);
8584 } // for (h < n_extra_holes)
8585
8586 // Copy the extra holes coordinates
8587 Original_extra_holes_coordinates = Extra_holes_coordinates;
8588
8589 // Set the flag to false
8590 First_time_compute_holes_left_by_halo_elements = false;
8591
8592 } // if (First_time_compute_holes_left_by_halo_elements)
8593 else
8594 {
8595 // Not the first time, then only copy the original extra holes
8596 // coordinates
8597 const unsigned n_original_extra_holes =
8598 Original_extra_holes_coordinates.size();
8599 for (unsigned h = 0; h < n_original_extra_holes; h++)
8600 {
8601 Vector<double> hole_coordinates = Original_extra_holes_coordinates[h];
8602 new_holes_coordinates.push_back(hole_coordinates);
8603 } // for (h < n_original_extra_holes)
8604 }
8605
8606 // Add the holes created by the halo elements adjacent to the shared
8607 // boundaries
8608 compute_holes_left_by_halo_elements_helper(new_holes_coordinates);
8609
8610 // Update the holes information, only use the coordinate inside the
8611 // poylgons that define the new domain
8612 update_holes_information_helper(polygons_pt, new_holes_coordinates);
8613
8614 // tachidok Clear the storage by now
8615 // new_holes_coordinates.clear();
8616
8617 // Now copy the info. in the extra holes coordinates container
8618 Extra_holes_coordinates = new_holes_coordinates;
8619
8620 // Do not delete halo(ed) info., this will be "deleted"
8621 // automatically by not passing that information to the new adapted
8622 // mesh. Once the transfer of target areas is performed the halo(ed)
8623 // information is no longer required
8624 }
8625
8626 //======================================================================
8627 // Take the polylines from the shared boundaries and the boundaries
8628 // to create polygons
8629 //======================================================================
8630 template<class ELEMENT>
8633 Vector<TriangleMeshPolygon*>& polygons_pt)
8634 {
8635 // Each vector of polylines (curve) is already sorted, it means that
8636 // all the polylines on the vector polylines_pt[i] point to the same
8637 // direction
8638
8639 // --- Using this fact we should compare the first and last points from
8640 // these arrays of polylines (curves) and compare with the others
8641 // vectors of polylines (curves) end points
8642 // --- Once created a closed curve create a polygon
8643
8644 // The number of curves
8645 const unsigned ncurves = polylines_pt.size();
8646
8647 // The number of non sorted curves
8648 const unsigned nunsorted_curves = ncurves;
8649 // The number of sorted curves
8650 unsigned nsorted_curves = 0;
8651
8652 // Vector to know which ncurve is already done
8653 std::vector<bool> done_curve(ncurves);
8654
8655 do
8656 {
8657 // The list where to add the curves so that they be contiguous
8658 std::list<Vector<TriangleMeshPolyLine*>> list_building_polygon_pt;
8659#ifdef PARANOID
8660 // Flag to indicate that a root curve was found
8661 bool root_curve_found = false;
8662#endif
8663
8664 // The index for the root_curve (we use it in further iterations as the
8665 // starting index so we dont need to search in already done curves)
8666 unsigned root_curve_idx = 0;
8667
8668 // Get the root curve
8669 for (unsigned ic = 0; ic < ncurves; ic++)
8670 {
8671 if (!done_curve[ic])
8672 {
8673 root_curve_idx = ic;
8674 nsorted_curves++;
8675#ifdef PARANOID
8676 root_curve_found = true;
8677#endif
8678 done_curve[ic] = true;
8679 // ... break the loop
8680 break;
8681 }
8682 }
8683
8684#ifdef PARANOID
8685 if (!root_curve_found)
8686 {
8687 std::stringstream err;
8688 err << "The root curve to create a polygon from the shared and "
8689 << "original boundaries was not found!!!\n";
8690 throw OomphLibError(err.str(),
8691 "TriangleMesh::create_tmp_polygons_helper()",
8692 OOMPH_EXCEPTION_LOCATION);
8693 }
8694#endif
8695
8696 // Get the root curve
8697 Vector<TriangleMeshPolyLine*> root_curve_pt =
8698 polylines_pt[root_curve_idx];
8699
8700 // Add the root curve to the list
8701 list_building_polygon_pt.push_back(root_curve_pt);
8702
8703 // Get the initial and final vertices from the root curve
8704 Vector<double> root_curve_initial_vertex(2);
8705 Vector<double> root_curve_final_vertex(2);
8706
8707 // We need to get the number of polylines that compose the root curve
8708 const unsigned nroot_curve_polyline = root_curve_pt.size();
8709 // ... and now get the initial and final vertex
8710 root_curve_pt[0]->initial_vertex_coordinate(root_curve_initial_vertex);
8711 root_curve_pt[nroot_curve_polyline - 1]->final_vertex_coordinate(
8712 root_curve_final_vertex);
8713
8714 // First check if it already create a polygon
8715 double diff =
8716 ((root_curve_initial_vertex[0] - root_curve_final_vertex[0]) *
8717 (root_curve_initial_vertex[0] - root_curve_final_vertex[0])) +
8718 ((root_curve_initial_vertex[1] - root_curve_final_vertex[1]) *
8719 (root_curve_initial_vertex[1] - root_curve_final_vertex[1]));
8720 diff = sqrt(diff);
8722 {
8723 // The polyline already create a Polygon, then create it!!!
8724 // Create the curve section representation of the current root curve
8725 Vector<TriangleMeshCurveSection*> curve_section_pt(
8726 nroot_curve_polyline);
8727
8728 // Copy the polylines into its curve section representation
8729 for (unsigned i = 0; i < nroot_curve_polyline; i++)
8730 {
8731 curve_section_pt[i] = root_curve_pt[i];
8732 }
8733
8734 // ... and create the Polygon
8735 TriangleMeshPolygon* new_polygon_pt =
8736 new TriangleMeshPolygon(curve_section_pt);
8737
8738 // Mark the polygon for deletion (in the destructor)
8739 this->Free_polygon_pt.insert(new_polygon_pt);
8740
8741 // Add the polygon to the output polygons
8742 polygons_pt.push_back(new_polygon_pt);
8743 } // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
8744 // when the curve creates a Polygon by itself
8745 else
8746 {
8747 // Flag to continue iterating while curves be added to the left
8748 // or right of the list of curves
8749 bool added_curve = false;
8750#ifdef PARANOID
8751 // Flag to know if the "loop" finish because a polygon was
8752 // created or because no more curves can be added to the left or
8753 // right
8754 bool polygon_created = false;
8755#endif
8756 do
8757 {
8758 added_curve = false;
8759 // If the root curve does not create a closed polygon then add curves
8760 // to the left or right until the curves create a closed polygon
8761 for (unsigned ic = root_curve_idx + 1; ic < ncurves; ic++)
8762 {
8763 if (!done_curve[ic])
8764 {
8765 // Get the current curve
8766 Vector<TriangleMeshPolyLine*> current_curve_pt = polylines_pt[ic];
8767
8768 // We need to get the number of polylines that compose the
8769 // current curve
8770 const unsigned ncurrent_curve_polyline = current_curve_pt.size();
8771
8772 // ... and get the initial and final coordinates for the current
8773 // curve
8774 Vector<double> current_curve_initial_vertex(2);
8775 Vector<double> current_curve_final_vertex(2);
8776
8777 current_curve_pt[0]->initial_vertex_coordinate(
8778 current_curve_initial_vertex);
8779 current_curve_pt[ncurrent_curve_polyline - 1]
8780 ->final_vertex_coordinate(current_curve_final_vertex);
8781
8782 // ---------------------------------------------------------------
8783 // Start adding curves to the left or right
8784 // ---------------------------------------------------------------
8785 diff = ((current_curve_final_vertex[0] -
8786 root_curve_initial_vertex[0]) *
8787 (current_curve_final_vertex[0] -
8788 root_curve_initial_vertex[0])) +
8789 ((current_curve_final_vertex[1] -
8790 root_curve_initial_vertex[1]) *
8791 (current_curve_final_vertex[1] -
8792 root_curve_initial_vertex[1]));
8793 diff = sqrt(diff);
8794 // CURRENT curve to the LEFT of the ROOT curve
8796 {
8797 // Add the current curve to the left
8798 list_building_polygon_pt.push_front(current_curve_pt);
8799 // Mark the curve as done
8800 done_curve[ic] = true;
8801 // Update the initial vertex values
8802 root_curve_initial_vertex[0] = current_curve_initial_vertex[0];
8803 root_curve_initial_vertex[1] = current_curve_initial_vertex[1];
8804 // Increase the number of sorted curves
8805 nsorted_curves++;
8806 // Set the flag to indicate that a curve was added to the list
8807 added_curve = true;
8808 break;
8809 }
8810
8811 diff = ((current_curve_initial_vertex[0] -
8812 root_curve_initial_vertex[0]) *
8813 (current_curve_initial_vertex[0] -
8814 root_curve_initial_vertex[0])) +
8815 ((current_curve_initial_vertex[1] -
8816 root_curve_initial_vertex[1]) *
8817 (current_curve_initial_vertex[1] -
8818 root_curve_initial_vertex[1]));
8819 diff = sqrt(diff);
8820 // CURRENT curve to the LEFT of the ROOT curve but INVERTED
8822 {
8823 Vector<TriangleMeshPolyLine*> tmp_curve_pt(
8824 ncurrent_curve_polyline);
8825 // Reverse each polyline and back them up
8826 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8827 {
8828 current_curve_pt[it]->reverse();
8829 tmp_curve_pt[it] = current_curve_pt[it];
8830 }
8831 // Now copy them back but in reverse order
8832 unsigned count = 0;
8833 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8834 {
8835 current_curve_pt[count] = tmp_curve_pt[i];
8836 }
8837 // Add the current curve to the left
8838 list_building_polygon_pt.push_front(current_curve_pt);
8839 // Mark the curve as done
8840 done_curve[ic] = true;
8841 // Update the initial vertex values
8842 root_curve_initial_vertex[0] = current_curve_final_vertex[0];
8843 root_curve_initial_vertex[1] = current_curve_final_vertex[1];
8844 // Increase the number of sorted curves
8845 nsorted_curves++;
8846 // Set the flag to indicate that a curve was added to the list
8847 added_curve = true;
8848 break;
8849 }
8850
8851 diff = ((current_curve_initial_vertex[0] -
8852 root_curve_final_vertex[0]) *
8853 (current_curve_initial_vertex[0] -
8854 root_curve_final_vertex[0])) +
8855 ((current_curve_initial_vertex[1] -
8856 root_curve_final_vertex[1]) *
8857 (current_curve_initial_vertex[1] -
8858 root_curve_final_vertex[1]));
8859 diff = sqrt(diff);
8860 // CURRENT curve to the RIGHT of the ROOT curve
8862 {
8863 // Add the current curve to the right
8864 list_building_polygon_pt.push_back(current_curve_pt);
8865 // Mark the curve as done
8866 done_curve[ic] = true;
8867 // Update the initial vertex values
8868 root_curve_final_vertex[0] = current_curve_final_vertex[0];
8869 root_curve_final_vertex[1] = current_curve_final_vertex[1];
8870 // Increase the number of sorted curves
8871 nsorted_curves++;
8872 // Set the flag to indicate that a curve was added to the list
8873 added_curve = true;
8874 break;
8875 }
8876
8877 diff =
8878 ((current_curve_final_vertex[0] - root_curve_final_vertex[0]) *
8879 (current_curve_final_vertex[0] - root_curve_final_vertex[0])) +
8880 ((current_curve_final_vertex[1] - root_curve_final_vertex[1]) *
8881 (current_curve_final_vertex[1] - root_curve_final_vertex[1]));
8882 diff = sqrt(diff);
8883 // CURRENT curve to the RIGHT of the ROOT curve but INVERTED
8885 {
8886 Vector<TriangleMeshPolyLine*> tmp_curve_pt(
8887 ncurrent_curve_polyline);
8888 // Reverse each polyline and back them up
8889 for (unsigned it = 0; it < ncurrent_curve_polyline; it++)
8890 {
8891 current_curve_pt[it]->reverse();
8892 tmp_curve_pt[it] = current_curve_pt[it];
8893 }
8894 // Now copy them back but in reverse order
8895 unsigned count = 0;
8896 for (int i = ncurrent_curve_polyline - 1; i >= 0; i--, count++)
8897 {
8898 current_curve_pt[count] = tmp_curve_pt[i];
8899 }
8900 // Add the current curve to the right
8901 list_building_polygon_pt.push_back(current_curve_pt);
8902 // Mark the curve as done
8903 done_curve[ic] = true;
8904 // Update the initial vertex values
8905 root_curve_final_vertex[0] = current_curve_initial_vertex[0];
8906 root_curve_final_vertex[1] = current_curve_initial_vertex[1];
8907 // Increase the number of sorted curves
8908 nsorted_curves++;
8909 // Set the flag to indicate that a curve was added to the list
8910 added_curve = true;
8911 break;
8912 }
8913
8914 } // if (!done_curve[ic])
8915
8916 } // for (ic < ncurves)
8917
8918 // After adding a curve check if it is possible to create a polygon
8919 double diff =
8920 ((root_curve_initial_vertex[0] - root_curve_final_vertex[0]) *
8921 (root_curve_initial_vertex[0] - root_curve_final_vertex[0])) +
8922 ((root_curve_initial_vertex[1] - root_curve_final_vertex[1]) *
8923 (root_curve_initial_vertex[1] - root_curve_final_vertex[1]));
8924 diff = sqrt(diff);
8926 {
8927 // If the curves already create a Polygon then go out of the
8928 // loop and create the Polygon
8929 added_curve = false;
8930#ifdef PARANOID
8931 // Set the flag to indicate that a Polygon has been created
8932 polygon_created = true;
8933#endif
8934 } // (diff <
8935 // ToleranceForVertexMismatchInPolygons::Tolerable_error)
8936 // when the curve creates a Polygon by itself
8937
8938 } while (added_curve);
8939
8940#ifdef PARANOID
8941 if (!polygon_created)
8942 {
8943 std::stringstream error_message;
8944 error_message
8945 << "It was no possible to create a TriangleMeshPolygon with "
8946 << "the input set of curves\n"
8947 << "These are the initial and final vertices in the current "
8948 << "sorted list of\nTriangleMeshPolyLines\n\n";
8949 Vector<double> init_vertex(2);
8950 Vector<double> final_vertex(2);
8951 unsigned icurve = 0;
8952 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8953 list_building_polygon_pt.begin();
8954 it != list_building_polygon_pt.end();
8955 it++, icurve++)
8956 {
8957 const unsigned ncurrent_curve_polyline = (*it).size();
8958 error_message << "TriangleMeshCurve #" << icurve << "\n"
8959 << "-----------------------------------\n";
8960 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++)
8961 {
8962 Vector<double> init_vertex(2);
8963 Vector<double> final_vertex(2);
8964 (*it)[ip]->initial_vertex_coordinate(init_vertex);
8965 (*it)[ip]->final_vertex_coordinate(final_vertex);
8966 error_message << "TriangleMeshPolyLine #" << ip << "\n"
8967 << "Initial vertex: (" << init_vertex[0] << ","
8968 << init_vertex[1] << ")\n"
8969 << "Final vertex: (" << final_vertex[0] << ","
8970 << final_vertex[1] << ")\n";
8971 } // for (ip < ncurrent_curve_polyline)
8972 } // for (it != list_building_polygon_pt.end())
8973
8974 throw OomphLibError(error_message.str(),
8975 "TriangleMesh::create_tmp_polygons_helper()",
8976 OOMPH_EXCEPTION_LOCATION);
8977
8978 } // if (!polygon_created)
8979#endif
8980
8981 // Create the polygon after joining the curves
8982 unsigned ntotal_polylines = 0;
8983 // Get the total number of polylines
8984 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8985 list_building_polygon_pt.begin();
8986 it != list_building_polygon_pt.end();
8987 it++)
8988 {
8989 ntotal_polylines += (*it).size();
8990 }
8991
8992 // Create the curve section representation of the curves on the list
8993 Vector<TriangleMeshCurveSection*> curve_section_pt(ntotal_polylines);
8994
8995 // Copy the polylines into its curve section representation
8996 unsigned counter = 0;
8997 for (std::list<Vector<TriangleMeshPolyLine*>>::iterator it =
8998 list_building_polygon_pt.begin();
8999 it != list_building_polygon_pt.end();
9000 it++)
9001 {
9002 const unsigned ncurrent_curve_polyline = (*it).size();
9003 for (unsigned ip = 0; ip < ncurrent_curve_polyline; ip++, counter++)
9004 {
9005 curve_section_pt[counter] = (*it)[ip];
9006 } // for (ip < ncurrent_curve_polyline)
9007 } // Loop over the list of polylines
9008
9009 // ... and create the Polygon
9010 TriangleMeshPolygon* new_polygon_pt =
9011 new TriangleMeshPolygon(curve_section_pt);
9012
9013 // Mark the polygon for deletion (in the destructor)
9014 this->Free_polygon_pt.insert(new_polygon_pt);
9015
9016 // Add the polygon to the output polygons
9017 polygons_pt.push_back(new_polygon_pt);
9018
9019 } // else
9020 // (diff < ToleranceForVertexMismatchInPolygons::Tolerable_error)
9021
9022 } while (nsorted_curves < nunsorted_curves);
9023 }
9024
9025 //======================================================================
9026 // Take the polylines from the original open curves and created
9027 // new temporaly representations of open curves with the bits of
9028 // original curves not overlapped by shared boundaries
9029 //======================================================================
9030 template<class ELEMENT>
9032 Vector<Vector<TriangleMeshPolyLine*>>& sorted_open_curves_pt,
9033 Vector<TriangleMeshPolyLine*>& unsorted_shared_to_internal_poly_pt,
9034 Vector<TriangleMeshOpenCurve*>& open_curves_pt)
9035 {
9036 // Here search for the connections of the open curves remaining as
9037 // open curves with the shared boundaries markes as internal
9038 const unsigned ninternal_open_curves = sorted_open_curves_pt.size();
9039
9040 // Once identified the connections created with the new internal
9041 // boundaries representations add them to the open curves container
9042 for (unsigned i = 0; i < ninternal_open_curves; i++)
9043 {
9044 // Create the curve section representation of the polylines
9045 const unsigned npoly = sorted_open_curves_pt[i].size();
9046 Vector<TriangleMeshCurveSection*> tmp_curve_section(npoly);
9047 for (unsigned j = 0; j < npoly; j++)
9048 {
9049 tmp_curve_section[j] = sorted_open_curves_pt[i][j];
9050 }
9051 // ... and create the Open Curve
9052 TriangleMeshOpenCurve* new_open_curve_pt =
9053 new TriangleMeshOpenCurve(tmp_curve_section);
9054
9055 // Mark the open curve for deletion (in the destructor)
9056 this->Free_open_curve_pt.insert(new_open_curve_pt);
9057
9058 // Add the open curve to the output open curves
9059 open_curves_pt.push_back(new_open_curve_pt);
9060
9061 } // (i < ninternal_open_curves)
9062 }
9063
9064 //======================================================================
9065 // Check for any possible connections that the array of sorted
9066 // nodes have with original boundary nodes, previous shared polyline
9067 // nodes or with itself polyline nodes. In case that there is a
9068 // connection, get the boundary id to which connects
9069 //======================================================================
9070 template<class ELEMENT>
9072 std::set<FiniteElement*>& element_in_processor_pt,
9073 const int& root_edge_bnd_id,
9074 std::map<std::pair<Node*, Node*>, bool>& overlapped_face,
9075 std::map<unsigned, std::map<Node*, bool>>&
9076 node_on_bnd_not_overlapped_by_shd_bnd,
9077 std::list<Node*>& current_polyline_nodes,
9078 std::map<unsigned, std::list<Node*>>& shared_bnd_id_to_sorted_list_node_pt,
9079 const unsigned& node_degree,
9080 Node*& new_node_pt,
9081 const bool called_from_load_balance)
9082 {
9083 // Initialize the flag to return
9084 int flag_to_return = -1;
9085
9086 // --------------------------------------------------------------------
9087 // First try to find a connection with any original boundary (keep
9088 // in mind the case when internal boundaries may be overlapped by
9089 // shared boundaries)
9090 // --------------------------------------------------------------------
9091
9092 // Check if the shared boundary is overlapping an internal boundary
9093 bool overlapping_internal_boundary = false;
9094 // The boundary id overlapped by the current shared boundary
9095 unsigned internal_overlaping_bnd_id = 0;
9096 if (root_edge_bnd_id != -1)
9097 {
9098 // Set the flat to true
9099 overlapping_internal_boundary = true;
9100 // Set the bnd id of the overlapped internal boundary
9101 internal_overlaping_bnd_id = static_cast<unsigned>(root_edge_bnd_id);
9102 } // if (root_edge_bnd_id != -1)
9103
9104 // ---------------------------------------------------------------
9105 // Check if the connection is with an original boundary by checking
9106 // if the new node is a boundary node, and it lives in an element
9107 // that is part of the domain
9108 // ---------------------------------------------------------------
9109 if (new_node_pt->is_on_boundary())
9110 {
9111 // Flag to indicate if the node lives in a non overlapped boundary
9112 bool is_node_living_in_non_overlapped_boundary = false;
9113
9114 // If the node is a boundary node then check in which boundary it
9115 // is
9116 const unsigned noriginal_bnd = this->initial_shared_boundary_id();
9117 for (unsigned bb = 0; bb < noriginal_bnd; bb++)
9118 {
9119 // If the shared boundary overlaps an internal boundary it will
9120 // be indicated by (root_edge_bnd_id != -1), the original
9121 // internal boundary that overlaps is given by the
9122 // root_edge_bnd_id value. We skip that original internal
9123 // boundary because the new node will be obviously ON the
9124 // internal boundary
9125 if (overlapping_internal_boundary)
9126 {
9127 // Is the node on boundary bb?
9128 if (new_node_pt->is_on_boundary(bb))
9129 {
9130 // If overlaping then check that the boundary is different
9131 // from the one that is being overlapped, or if overlapped
9132 // then check that the node is on an edge on the bb
9133 // boundary not overlapped by a shared boundary
9134 const bool on_bnd_edge_not_overlapped_by_shd_bnd =
9135 node_on_bnd_not_overlapped_by_shd_bnd[bb][new_node_pt];
9136 if (bb != internal_overlaping_bnd_id ||
9137 ((bb == internal_overlaping_bnd_id) &&
9138 (on_bnd_edge_not_overlapped_by_shd_bnd)))
9139 {
9140 // Is the node living in a non overlapped boundary
9141 if (bb != internal_overlaping_bnd_id)
9142 {
9143 is_node_living_in_non_overlapped_boundary = true;
9144 }
9145
9146 // Now we need to check that the node lies on a boundary
9147 // that still exist (the elements associated to the
9148 // boundary may have been removed at the mesh distribution
9149 // stage). The node may be still marked as a boundary node
9150 // but the boundary may not have elements associated.
9151
9152 // Get the number of elements in the boundary
9153 const unsigned n_bound_ele = this->nboundary_element(bb);
9154 if (n_bound_ele > 0)
9155 {
9156 // Check that node lies on a nonhalo element, those are
9157 // the elements used to update the domain representation
9158 for (unsigned e = 0; e < n_bound_ele; e++)
9159 {
9160 // Get the boundary bulk element
9161 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9162 // Check if the element will be retained, it means it
9163 // is a nonhalo element
9164 std::set<FiniteElement*>::iterator it =
9165 element_in_processor_pt.find(bulk_ele_pt);
9166 // If found then check if the node live in the element
9167 if (it != element_in_processor_pt.end())
9168 {
9169 // Found the node in the nonhalo face element
9170 bool found_node = false;
9171 // Get the face index
9172 int face_index = this->face_index_at_boundary(bb, e);
9173 // Create the face element
9174 FiniteElement* face_ele_pt =
9175 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9176 // Get the number of nodes in the face element
9177 const unsigned n_node_face = face_ele_pt->nnode();
9178 // Get the first and last node of the face element
9179 Node* first_node_pt = face_ele_pt->node_pt(0);
9180 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
9181 // Create the edge with the pair of nodes
9182 std::pair<Node*, Node*> tmp_edge =
9183 std::make_pair(first_node_pt, last_node_pt);
9184 // Check if the face element edge is overlapped by a
9185 // shared boundary
9186 // Is the face not overlapped?
9187 if (!overlapped_face[tmp_edge])
9188 {
9189 // Look for the node in the current face element
9190 for (unsigned n = 0; n < n_node_face; n++)
9191 {
9192 // Check for every individual node
9193 if (face_ele_pt->node_pt(n) == new_node_pt)
9194 {
9195 found_node = true;
9196 break;
9197 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9198 } // for (n < n_node_face)
9199 } // if (!overlapped_face[tmp_edge])
9200 // Free the memory of the face element
9201 delete face_ele_pt;
9202 if (found_node)
9203 {
9204 // return the first original boundary id found,
9205 // does not matter if the node lies on more than
9206 // one original boundary (with boundary
9207 // elements). This is the original boundary id
9208 // that will be used to create the connection
9209 flag_to_return = bb;
9210 return flag_to_return;
9211 } // if (found_node)
9212
9213 } // if (it!=element_in_processor_pt.end())
9214
9215 } // for (e < n_bound_ele)
9216
9217 } // if (n_bound_ele > 0)
9218
9219 } // if (bb != internal_overlaping_bnd_id ||
9220 // ((bb == internal_overlaping_bnd_id) &&
9221 // (on_bnd_edge_not_overlapped_by_shd_bnd)))
9222
9223 } // if (nod_pt->is_on_boundary(bb))
9224
9225 } // if (overlapping_internal_boundary)
9226 else
9227 {
9228 // Is the node on boundary bb?
9229 if (new_node_pt->is_on_boundary(bb))
9230 {
9231 // Now we need to check that the node lies on a boundary
9232 // that still exist (the elements associated to the boundary
9233 // may have been removed at the mesh distribution
9234 // stage). The node may be still marked as a boundary node
9235 // but the boundary may not have elements associated.
9236
9237 // Get the number of elements in the boundary
9238 const unsigned n_bound_ele = this->nboundary_element(bb);
9239 if (n_bound_ele > 0)
9240 {
9241 // Check that node lies on a nonhalo element, those are
9242 // the elements used to update the domain representation
9243 for (unsigned e = 0; e < n_bound_ele; e++)
9244 {
9245 // Get the boundary bulk element
9246 FiniteElement* bulk_ele_pt = this->boundary_element_pt(bb, e);
9247 // Check if the element will be retained, it means it is
9248 // a nonhalo element
9249 std::set<FiniteElement*>::iterator it =
9250 element_in_processor_pt.find(bulk_ele_pt);
9251 // If found then check if the node live in the element
9252 if (it != element_in_processor_pt.end())
9253 {
9254 // Found the node in the nonhalo face element
9255 bool found_node = false;
9256 // Get the face index
9257 int face_index = this->face_index_at_boundary(bb, e);
9258 // Create the face element
9259 FiniteElement* face_ele_pt =
9260 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
9261 // Get the number of nodes in the face element
9262 const unsigned n_node_face = face_ele_pt->nnode();
9263 // Get the first and last node of the face element
9264 Node* first_node_pt = face_ele_pt->node_pt(0);
9265 Node* last_node_pt = face_ele_pt->node_pt(n_node_face - 1);
9266 // Create the edge with the pair of nodes
9267 std::pair<Node*, Node*> tmp_edge =
9268 std::make_pair(first_node_pt, last_node_pt);
9269 // Check if the face element edge is overlapped by a
9270 // shared boundary
9271 // Is the face not overlapped?
9272 if (!overlapped_face[tmp_edge])
9273 {
9274 // Look for the node in the current face element
9275 for (unsigned n = 0; n < n_node_face; n++)
9276 {
9277 // Check for every individual node
9278 if (face_ele_pt->node_pt(n) == new_node_pt)
9279 {
9280 found_node = true;
9281 break;
9282 } // if (face_ele_pt->node_pt(n) == new_node_pt)
9283 } // for (n < n_node_face)
9284 } // if (!overlapped_face[tmp_edge])
9285 // Free the memory of the face element
9286 delete face_ele_pt;
9287 if (found_node)
9288 {
9289 // return the first original boundary id found, does
9290 // not matter if the node lies on more than one
9291 // original boundary (with boundary elements). This
9292 // is the original boundary id that will be used to
9293 // create the connection
9294 flag_to_return = bb;
9295 return flag_to_return;
9296 } // if (found_node)
9297
9298 } // if (it!=element_in_processor_pt.end())
9299
9300 } // for (e < n_bound_ele)
9301
9302 } // if (n_bound_ele > 0)
9303
9304 } // if (nod_pt->is_on_boundary(bb))
9305 } // else if (overlapping_internal_boundary)
9306 } // for (bb < noriginal_bnd)
9307
9308 // We will only reach this stage when the node was found to be
9309 // connected to an original boundary but the element(s) on that
9310 // boundary where the node should live are not part of the domain.
9311 // Think in a corner of a triangle which touches the boundary
9312 // which elements will not be part of the domain
9313
9314 // We need to break the currently forming polyline
9315 // flag_to_return = -3;
9316
9317 // We need to break the currently forming polyline if and only if
9318 // the boundary(ies) in which the node is living is(are) not an
9319 // overlapped boundary
9320 if (!overlapping_internal_boundary)
9321 {
9322 // If the boundary(ies) in which the node is living is(are) an
9323 // overlapped boundary then break the break the formation of the
9324 // polyline
9325 flag_to_return = -3;
9326 }
9327 else
9328 {
9329 // The boundary is overlapped, if the node lives in a non
9330 // overlapped boundary then we can break the formation of the
9331 // polyline
9332 if (is_node_living_in_non_overlapped_boundary)
9333 {
9334 flag_to_return = -3;
9335 } // if (is_node_living_in_non_overlapped_boundar)y
9336
9337 } // if (!overlapping_internal_boundary)
9338
9339 } // if (new_node_pt->is_on_boundary())
9340
9341 // Return inmediately if the connection is with an original boundary
9342 // whose elements are still part of the domain
9343 if (flag_to_return >= 0)
9344 {
9345 return flag_to_return;
9346 }
9347
9348 // ----------------------------------------------------------------------
9349 // Secondly, if there is not a connection with any original
9350 // boundary, or if there is connection but with an original boundary
9351 // whose elements are not part of the domain, then check for
9352 // connections with previously created shared polylines
9353 // ----------------------------------------------------------------------
9354 // Store all the previous shared polylines to which the current
9355 // found is found to be connected
9356 Vector<unsigned> candidate_shared_bnd_to_connect;
9357 // Check for all the previous polylines except the current one
9358 for (std::map<unsigned, std::list<Node*>>::iterator it =
9359 shared_bnd_id_to_sorted_list_node_pt.begin();
9360 it != shared_bnd_id_to_sorted_list_node_pt.end();
9361 it++)
9362 {
9363 // Get the boundary id of the list of nodes that created the
9364 // polyline (the shared boundary id associated with the list of
9365 // nodes)
9366 const unsigned i_bnd_id = (*it).first;
9367 // Get an iterator pointer to the list of nodes of the shared
9368 // polyline
9369 std::list<Node*>::iterator it_list = (*it).second.begin();
9370 // Get the total number of nodes associated to the boundary
9371 const unsigned n_nodes = (*it).second.size();
9372 // Search for connections in the list of nodes
9373 for (unsigned i = 0; i < n_nodes; i++, it_list++)
9374 {
9375 // Is the node already part of any other shared boundary
9376 if ((*it_list) == new_node_pt)
9377 {
9378 // Include the i-th boundary id in the list of candidate
9379 // shared boundaries to connect
9380 candidate_shared_bnd_to_connect.push_back(i_bnd_id);
9381 // Break the look with the i-th shared boundary, check with
9382 // the others shared boundaries
9383 break;
9384 } // if ((*it_list) == new_node_pt)
9385
9386 } // for (i < nnodes)
9387
9388 } // Loop over the shared boundaries and associated nodes
9389
9390 // Get the number of candidate shared boundaries to connect
9391 const unsigned n_candidate_shared_bnd_to_connect =
9392 candidate_shared_bnd_to_connect.size();
9393
9394 // Is there a connection with any previous shared polyline
9395 if (n_candidate_shared_bnd_to_connect > 0)
9396 {
9397 // If called from load balance we do not need to check if the
9398 // shared boundary is part of the processor since it certanily is,
9399 // only the shared boundaries that are pare of the processor are
9400 // used to created connection when creating the new shared
9401 // boundaries in the load balance rutine
9402 if (called_from_load_balance)
9403 {
9404 return candidate_shared_bnd_to_connect[0];
9405 }
9406
9407 // We need to ensure that the shared boundary to which we are
9408 // connecting is part of the current processor, if none of the
9409 // found shared bundaries is in the current processor then return
9410 // the flag for "connection with boundary not in the current
9411 // processor"
9412
9413 // Store the shared boundaries associated with the current processor
9414 Vector<unsigned> shared_bound_in_this_proc;
9415
9416 // Get the shared boundaries associated with the current processor
9417 shared_boundaries_in_this_processor(shared_bound_in_this_proc);
9418
9419 // If any of the candidate shared boundaries to connect is in the
9420 // current processor then return that shared boundary id
9421
9422 // The number of shared boundaries in the current processor
9423 const unsigned n_shared_bound_in_this_proc =
9424 shared_bound_in_this_proc.size();
9425
9426 // Loop over the candidate shared boundaries to connect
9427 for (unsigned i = 0; i < n_candidate_shared_bnd_to_connect; i++)
9428 {
9429 // Get the i-th candidate shared boundary to connect
9430 const unsigned i_candidate_shared_bnd =
9431 candidate_shared_bnd_to_connect[i];
9432
9433 // Loop over the shared boundaries in the current processor
9434 for (unsigned j = 0; j < n_shared_bound_in_this_proc; j++)
9435 {
9436 // Is the candidate boundary a shared boundary in this processor?
9437 if (i_candidate_shared_bnd == shared_bound_in_this_proc[j])
9438 {
9439 // Return the candidate shared boundary
9440 flag_to_return = i_candidate_shared_bnd;
9441 return flag_to_return;
9442 } // The candidate shared boundary is a boundary in the
9443 // current processor
9444
9445 } // for (j < n_shared_bound_in_this_proc)
9446
9447 } // for (i < n_candidate_shared_bnd_to_connect)
9448
9449 // If non of the candidate shared boundaries to connect is in the
9450 // current processor the mark that we need to stop the addition of
9451 // vertices at this side of the polyline
9452 flag_to_return = -3;
9453
9454 } // if (n_candidate_shared_bnd_to_connect > 0)
9455
9456 // Return inmediately if the connection is with a previuos shared
9457 // boundary
9458 if (flag_to_return >= 0)
9459 {
9460 return flag_to_return;
9461 }
9462
9463 // ------------------------------------------------------------------
9464 // Finally,check for connections with the same polyline (the shared
9465 // boundary that is being constructed). We are trying to avoid loops
9466 // or connections with the same shared boundary that is why this is
9467 // checked at the end
9468 // ------------------------------------------------------------------
9469 unsigned nrepeated = 0;
9470 for (std::list<Node*>::iterator it_list = current_polyline_nodes.begin();
9471 it_list != current_polyline_nodes.end();
9472 it_list++)
9473 {
9474 // There must be at least one repeated node (the one that we have
9475 // just added, and it should be the first or last node)
9476 if ((*it_list) == new_node_pt)
9477 {
9478 nrepeated++;
9479 }
9480 }
9481 // If the number of repeated nodes is greater than one then the
9482 // polyline has a connection with itself
9483 if (nrepeated > 1)
9484 {
9485 // Return the flag value to indicate connection with itself, we
9486 // can not return the boundary id of the current polyline since it
9487 // has not been already assigned
9488 flag_to_return = -2;
9489 }
9490
9491 // If there is no connection at all check the degree of the node, if
9492 // it is greater than 2 then return the flag to stop adding nodes
9493 // after this one
9494 if (node_degree > 2)
9495 {
9496 flag_to_return = -3;
9497 }
9498
9499 // Return the flag
9500 return flag_to_return;
9501 }
9502
9503 //======================================================================
9504 // Establish the connections of the polylines previously
9505 // marked as having connections. This connections were created in the
9506 // function TriangleMesh::create_polylines_from_halo_elements_helper().
9507 // In case of doing load balancing the connections were created by the
9508 // function RefineableTriangleMesh::create_new_shared_boundaries()
9509 // ======================================================================
9510 template<class ELEMENT>
9512 {
9513 // Get the rank of the current processor
9514 const unsigned my_rank = this->communicator_pt()->my_rank();
9515
9516 // Get the shared curves associated with this processor
9517 Vector<Vector<TriangleMeshPolyLine*>> shared_curves_pt =
9518 this->Shared_boundary_polyline_pt[my_rank];
9519
9520 // Loop through the shared boundaries on the current processor and
9521 // check if they are marked to create a connection
9522 const unsigned ncurves = shared_curves_pt.size();
9523 for (unsigned icurve = 0; icurve < ncurves; icurve++)
9524 {
9525 // Get the number of polylines in the current shared curve
9526 const unsigned npoly = shared_curves_pt[icurve].size();
9527 for (unsigned ipoly = 0; ipoly < npoly; ipoly++)
9528 {
9529 // Get the polyline representation of the shared boundary
9530 TriangleMeshPolyLine* shd_poly_pt = shared_curves_pt[icurve][ipoly];
9531
9532 // Get the boundary id of the current polyline
9533 const unsigned bound_id = shd_poly_pt->boundary_id();
9534
9535 // Is the left vertex connected
9536 const bool is_connected_to_the_left =
9537 shd_poly_pt->is_initial_vertex_connected();
9538
9539 // Is the right vertex connected
9540 const bool is_connected_to_the_right =
9541 shd_poly_pt->is_final_vertex_connected();
9542
9543 // -----------------------------------------------------------------
9544 // If there is a connection at one of the ends we need to
9545 // establish that connection
9546 if (is_connected_to_the_left || is_connected_to_the_right)
9547 {
9548 // Now get the new left and right vertices of the shared
9549 // polyline
9550 const unsigned n_vertex = shd_poly_pt->nvertex();
9551
9552 // Now get the polylines to where the current shared boundary is
9553 // connected and create the connections
9554
9555 // --------------------------------------------------------------
9556 // Connection to the left
9557 if (is_connected_to_the_left)
9558 {
9559 // Get the unsigned version of the bound id to connect to
9560 // the left
9561 const unsigned uconnection_to_the_left =
9562 shd_poly_pt->initial_vertex_connected_bnd_id();
9563
9564 // The pointer to the boundary to connect
9565 TriangleMeshPolyLine* poly_to_connect_pt = 0;
9566
9567 // Flag to indicate we are trying to connect to an split
9568 // boundary
9569 bool connecting_to_an_split_boundary = false;
9570
9571 // Flag to indicate we are trying to connecto to an internal
9572 // boundary that is overlaped by a shared boundary
9573 bool connecting_to_an_overlaped_boundary = false;
9574
9575 // Check if the connection is with itself
9576 if (uconnection_to_the_left == bound_id)
9577 {
9578 // Set the pointer to the polyline to connect
9579 poly_to_connect_pt = shd_poly_pt;
9580 }
9581 else
9582 {
9583 // Get the initial shared boundary ids
9584 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
9585 // Check if the boundary to connect is a shared polyline
9586 if (uconnection_to_the_left >= initial_shd_bnd_id)
9587 {
9588 // Get the polyline pointer representing the destination
9589 // boundary
9590 poly_to_connect_pt =
9591 boundary_polyline_pt(uconnection_to_the_left);
9592 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
9593 else
9594 {
9595 // If we are going to connect to an original boundary
9596 // verify if the boundary was splitted during the
9597 // distribution process to consider all the chunks
9598 // (sub-polylines) of the boundary
9599 if (boundary_was_splitted(uconnection_to_the_left))
9600 {
9601 connecting_to_an_split_boundary = true;
9602 } // if (boundary_was_splitted(uconnection_to_the_left))
9603
9604 // If we are going to connect to an original boundary
9605 // verify if the boundary, or any of its chunks is
9606 // marked to be overlapped by a shared boundary, if that
9607 // is the case we first check for connections in the
9608 // shared boundary that overlaps the internal boundary,
9609 // or the chunks, and then check for connections in the
9610 // original boundary
9611 if (connecting_to_an_split_boundary)
9612 {
9613 // Get the number of chucks that represent the
9614 // destination boundary
9615 const unsigned n_sub_poly =
9616 nboundary_subpolylines(uconnection_to_the_left);
9617 // Now loop over the chunks of the destination
9618 // boundary and if any of them is marked to be
9619 // overlaped by a shared boundary then set the flag
9620 // and break the loop
9621 for (unsigned ii = 0; ii < n_sub_poly; ii++)
9622 {
9623 if (boundary_marked_as_shared_boundary(
9624 uconnection_to_the_left, ii))
9625 {
9626 // Mark the boundary as being overlaped by a
9627 // shared boundary
9628 connecting_to_an_overlaped_boundary = true;
9629 // Break, no need to look for more overlapings
9630 break;
9631 } // if (boundary_marked_as_shared_boundary(...))
9632 } // for (ii < n_sub_poly)
9633 } // if (connecting_to_an_split_boundary)
9634 else
9635 {
9636 // If not connecting to an split boundary then check
9637 // if the whole destination boundary is overlaped by
9638 // an internal boundary
9639 if (boundary_marked_as_shared_boundary(
9640 uconnection_to_the_left, 0))
9641 {
9642 // Mark the boundary as being overlaped by a shared
9643 // boundary
9644 connecting_to_an_overlaped_boundary = true;
9645 } // if (boundary_marked_as_shared_boundary(...))
9646 } // else if (connecting_to_an_split_boundary)
9647
9648 // If we are connecting neither to an split boundary nor
9649 // an overlaped boundary then get the pointer to the
9650 // original boundary
9651 if (!(connecting_to_an_split_boundary ||
9652 connecting_to_an_overlaped_boundary))
9653 {
9654 // Get the polyline pointer representing the
9655 // destination boundary
9656 poly_to_connect_pt =
9657 boundary_polyline_pt(uconnection_to_the_left);
9658 } // else if (NOT split, NOT overlaped)
9659 } // else if (uconnection_to_the_left >= initial_shd_bnd_id)
9660
9661 } // else if (uconnection_to_the_left == bound_id)
9662
9663#ifdef PARANOID
9664 // If we are not connecting to an original boundary
9665 // (connecting to the same shared boundary or to another
9666 // shared boundary) then the boundary should not be marked
9667 // as split
9668 if (!connecting_to_an_split_boundary)
9669 {
9670 if (boundary_was_splitted(uconnection_to_the_left))
9671 {
9672 std::stringstream error;
9673 error
9674 << "The current shared boundary (" << bound_id << ") was "
9675 << "marked to have a connection\nto the left with the "
9676 << "boundary (" << uconnection_to_the_left << ").\n"
9677 << "The problem is that the destination boundary (possibly\n"
9678 << "another shared boundary) is marked to be split\n"
9679 << "There should not be split shared boundaries\n\n";
9680 throw OomphLibError(
9681 error.str(),
9682 "TriangleMesh::create_shared_polylines_connections()",
9683 OOMPH_EXCEPTION_LOCATION);
9684 }
9685 } // if (!connecting_to_an_split_boundary)
9686#endif
9687
9688 // Now look for the vertex number on the destination
9689 // boundary(ies) -- in case that the boundary was split ---
9690
9691 // Do not check for same orientation, that was previously
9692 // worked by interchanging the connections boundaries (if
9693 // necessary)
9694
9695 // Get the left vertex in the shared boundary
9696 Vector<double> shd_bnd_left_vertex =
9697 shd_poly_pt->vertex_coordinate(0);
9698
9699 // If the boundary was not split then ...
9700 if (!connecting_to_an_split_boundary)
9701 {
9702 // ... check if the boundary is marked to be overlaped by
9703 // a shared boundary
9704 if (!connecting_to_an_overlaped_boundary)
9705 {
9706 // If that is not the case then we can safely look for
9707 // the vertex number on the destination boundar
9708 unsigned vertex_index = 0;
9709
9710 const bool found_vertex_index =
9711 get_connected_vertex_number_on_destination_polyline(
9712 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9713
9714 // If we could not find the vertex index to connect then
9715 // we are in trouble
9716 if (!found_vertex_index)
9717 {
9718 std::stringstream error;
9719 error
9720 << "The current shared boundary (" << bound_id << ") was "
9721 << "marked to have a connection\nto the left with the "
9722 << "boundary (" << uconnection_to_the_left << ").\n"
9723 << "The problem is that the left vertex of the current\n"
9724 << "shared boundary is not in the list of vertices of the\n"
9725 << "boundary to connect.\n\n"
9726 << "This is the left vertex of the current shared "
9727 "boundary\n"
9728 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9729 << shd_bnd_left_vertex[1] << ")\n\n"
9730 << "This is the list of vertices on the destination "
9731 << "boundary\n";
9732 const unsigned n_v = poly_to_connect_pt->nvertex();
9733 for (unsigned i = 0; i < n_v; i++)
9734 {
9735 Vector<double> cvertex =
9736 poly_to_connect_pt->vertex_coordinate(i);
9737 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9738 << cvertex[1] << ")\n";
9739 }
9740 throw OomphLibError(
9741 error.str(),
9742 "TriangleMesh::create_shared_polylines_connections()",
9743 OOMPH_EXCEPTION_LOCATION);
9744 } // if (!found_vertex_index)
9745
9746 // Create the connection, the left vertex of the current
9747 // shared boundary is connected with the vertex_index-th
9748 // vertex on the destination boundary
9750 poly_to_connect_pt, vertex_index);
9751
9752 } // if (!connecting_to_an_overlaped_boundary)
9753 else
9754 {
9755 // If the boundary is marked to be overlaped by a shared
9756 // boundary then get that shared boundary and look for
9757 // the connection in that boundary
9758
9759 // The vertex where to store the index to connect
9760 unsigned vertex_index = 0;
9761 // A flag to indicate if the connection was found
9762 bool found_vertex_index = false;
9763
9764 // Get the shared boundary id that is overlaping the
9765 // internal boundary
9766 Vector<unsigned> dst_shd_bnd_ids;
9767 get_shared_boundaries_overlapping_internal_boundary(
9768 uconnection_to_the_left, dst_shd_bnd_ids);
9769
9770 // Get the number of shared polylines that were found to
9771 // overlap the internal boundary
9772 const unsigned n_shd_bnd_overlap_int_bnd =
9773 dst_shd_bnd_ids.size();
9774
9775 // Loop over the shared boundaries that overlap the
9776 // internal boundary and look for the vertex to connect
9777 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9778 {
9779 // Get the shared polyline
9780 const unsigned new_connection_to_the_left =
9781 dst_shd_bnd_ids[ss];
9782
9783 // Get the shared polyline that is overlaping the
9784 // internal boundary
9785 poly_to_connect_pt =
9786 boundary_polyline_pt(new_connection_to_the_left);
9787
9788 if (poly_to_connect_pt != 0)
9789 {
9790 // Look for the vertex number in the destination
9791 // shared polyline
9792 found_vertex_index =
9793 get_connected_vertex_number_on_destination_polyline(
9794 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9795 } // if (poly_to_connect_pt!=0)
9796
9797 // If we have found the vertex to connect then
9798 // break the loop
9799 if (found_vertex_index)
9800 {
9801 break;
9802 } // if (found_vertex_index)
9803
9804 } // for (ss < n_shd_bnd_overlaping_int_bnd)
9805
9806#ifdef PARANOID
9807 // If we could not find the vertex index to connect then
9808 // we are in trouble
9809 if (!found_vertex_index)
9810 {
9811 std::stringstream error;
9812 error
9813 << "The current shared boundary (" << bound_id << ") was "
9814 << "marked to have a connection\nto the left with the "
9815 << "boundary (" << uconnection_to_the_left << ").\n"
9816 << "This last boundary is marked to be overlaped by "
9817 << "shared boundaries\n"
9818 << "The problem is that the left vertex of the current\n"
9819 << "shared boundary is not in the list of vertices of the\n"
9820 << "boundary to connect.\n\n"
9821 << "This is the left vertex of the current shared "
9822 "boundary\n"
9823 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9824 << shd_bnd_left_vertex[1] << ")\n\n"
9825 << "This is the list of vertices on the destination "
9826 << "boundary\n";
9827 Vector<unsigned> dst_shd_bnd_ids;
9828 get_shared_boundaries_overlapping_internal_boundary(
9829 uconnection_to_the_left, dst_shd_bnd_ids);
9830 const unsigned n_shd_bnd_overlap_int_bnd =
9831 dst_shd_bnd_ids.size();
9832 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
9833 {
9834 const unsigned new_connection_to_the_left =
9835 dst_shd_bnd_ids[ss];
9836 poly_to_connect_pt =
9837 boundary_polyline_pt(new_connection_to_the_left);
9838 if (poly_to_connect_pt != 0)
9839 {
9840 const unsigned shd_bnd_id_overlap =
9841 poly_to_connect_pt->boundary_id();
9842 error << "Shared boundary id(" << shd_bnd_id_overlap
9843 << ")\n";
9844 const unsigned n_v = poly_to_connect_pt->nvertex();
9845 for (unsigned i = 0; i < n_v; i++)
9846 {
9847 Vector<double> cvertex =
9848 poly_to_connect_pt->vertex_coordinate(i);
9849 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9850 << cvertex[1] << ")\n";
9851 }
9852 } // if (poly_to_connect_pt != 0)
9853 } // for (ss < n_shd_bnd_overlap_int_bnd)
9854
9855 throw OomphLibError(
9856 error.str(),
9857 "TriangleMesh::create_shared_polylines_connections()",
9858 OOMPH_EXCEPTION_LOCATION);
9859
9860 } // if (!found_vertex_index)
9861#endif
9862
9863 // Create the connection, the left vertex of the current
9864 // shared boundary is connected with the vertex_index-th
9865 // vertex on the destination boundary
9867 poly_to_connect_pt, vertex_index);
9868
9869 } // else if (!connecting_to_an_overlaped_boundary)
9870
9871 } // if (!connecting_to_an_split_boundary)
9872 else
9873 {
9874 // If the boundary was split then we need to look for the
9875 // vertex in the sub-polylines
9876
9877 // Get the sub-polylines vector
9878 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
9879 boundary_subpolylines(uconnection_to_the_left);
9880
9881 // Get the number of sub-polylines
9882 const unsigned nsub_poly = tmp_vector_subpolylines.size();
9883#ifdef PARANOID
9884 if (nsub_poly <= 1)
9885 {
9886 std::ostringstream error_message;
9887 error_message
9888 << "The boundary (" << uconnection_to_the_left << ") was "
9889 << "marked to be splitted but\n"
9890 << "there are only (" << nsub_poly << ") polylines to "
9891 << "represent it.\n";
9892 throw OomphLibError(
9893 error_message.str(),
9894 "TriangleMesh::create_shared_polylines_connections()",
9895 OOMPH_EXCEPTION_LOCATION);
9896 } // if (nsub_poly <= 1)
9897#endif
9898 // We need to check if the boundary is marked to be
9899 // overlaped by an internal boundary, if that is the case
9900 // we need to check for each indivual subpolyline, and for
9901 // those overlaped by a shared polyline look for the
9902 // vertex in the shared polyline representation instead of
9903 // the original subpolyline
9904
9905 // ... check if the boundary is marked to be overlaped by
9906 // a shared boundary
9907 if (!connecting_to_an_overlaped_boundary)
9908 {
9909 // We can work without checking the subpolylines
9910 // individually
9911
9912 // The vertex where to store the index to connect
9913 unsigned vertex_index = 0;
9914 // The subpoly number to connect
9915 unsigned sub_poly_to_connect = 0;
9916 // A flag to indicate if the connection was found
9917 bool found_vertex_index = false;
9918
9919 // Look for the vertex number to connect on each of the
9920 // subpolyines
9921 for (unsigned isub = 0; isub < nsub_poly; isub++)
9922 {
9923 // Assign the pointer to the sub-polyline
9924 poly_to_connect_pt = tmp_vector_subpolylines[isub];
9925 // Search for the vertex in the current sub-polyline
9926 found_vertex_index =
9927 get_connected_vertex_number_on_destination_polyline(
9928 poly_to_connect_pt, shd_bnd_left_vertex, vertex_index);
9929 // If we have found the vertex to connect then break the
9930 // loop
9931 if (found_vertex_index)
9932 {
9933 // But first save the subpoly number (chunk), that
9934 // will be used to perform the connection
9935 sub_poly_to_connect = isub;
9936 break;
9937 } // if (found_vertex_index)
9938 } // for (isub < nsub_poly)
9939
9940#ifdef PARANOID
9941 // If we could not find the vertex index to connect then
9942 // we are in trouble
9943 if (!found_vertex_index)
9944 {
9945 std::stringstream error;
9946 error
9947 << "The current shared boundary (" << bound_id << ") was "
9948 << "marked to have a connection\nto the left with the "
9949 << "boundary (" << uconnection_to_the_left << ").\n"
9950 << "The problem is that the left vertex of the current\n"
9951 << "shared boundary is not in the list of vertices of any\n"
9952 << "of the sub polylines that represent the boundary to\n"
9953 << "connect.\n\n"
9954 << "This is the left vertex of the current shared "
9955 "boundary\n"
9956 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
9957 << shd_bnd_left_vertex[1] << ")\n\n"
9958 << "This is the list of vertices on the destination "
9959 << "boundary\n";
9960 for (unsigned p = 0; p < nsub_poly; p++)
9961 {
9962 error << "Subpolyline #(" << p << ")\n";
9963 poly_to_connect_pt = tmp_vector_subpolylines[p];
9964 const unsigned n_v = poly_to_connect_pt->nvertex();
9965 for (unsigned i = 0; i < n_v; i++)
9966 {
9967 Vector<double> cvertex =
9968 poly_to_connect_pt->vertex_coordinate(i);
9969 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
9970 << cvertex[1] << ")\n";
9971 }
9972 } // for (p < nsub_poly)
9973 throw OomphLibError(
9974 error.str(),
9975 "TriangleMesh::create_shared_polylines_connections()",
9976 OOMPH_EXCEPTION_LOCATION);
9977 } // if (!found_vertex_index)
9978#endif
9979
9980 // Create the connection, the left vertex of the current
9981 // shared boundary is connected with the vertex_index-th
9982 // vertex of sub_poly_to_connect-th subpolyline of the
9983 // destination boundary
9985 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
9986
9987 } // if (!connecting_to_an_overlaped_boundary)
9988 else
9989 {
9990 // We first look on the shared boundaries that overlap
9991 // the internal boundaries and the look for the
9992 // sub-polylines that are not marked as being overlaped
9993 // by shared boundaries
9994
9995 // The vertex where to store the index to connect
9996 unsigned vertex_index = 0;
9997 // The subpoly number to connect
9998 unsigned sub_poly_to_connect = 0;
9999 // A flag to indicate if the connection was found
10000 bool found_vertex_index = false;
10001
10002 // Get the shared boundaries id that are overlaping the
10003 // internal boundary
10004 Vector<unsigned> dst_shd_bnd_ids;
10005 get_shared_boundaries_overlapping_internal_boundary(
10006 uconnection_to_the_left, dst_shd_bnd_ids);
10007
10008 // Get the number of shared polylines that were found to
10009 // overlap the internal boundary
10010 const unsigned n_shd_bnd_overlap_int_bnd =
10011 dst_shd_bnd_ids.size();
10012
10013 // Loop over the shared boundaries that overlap the
10014 // internal boundary and look for the vertex to connect
10015 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10016 {
10017 // Get the shared polyline
10018 const unsigned new_connection_to_the_left =
10019 dst_shd_bnd_ids[ss];
10020
10021 // Make sure that the destination polyline is not the
10022 // same as the current shared polyline
10023 if (bound_id != new_connection_to_the_left)
10024 {
10025 // Get the shared polyline that is overlaping the
10026 // internal boundary
10027 poly_to_connect_pt =
10028 boundary_polyline_pt(new_connection_to_the_left);
10029
10030 if (poly_to_connect_pt != 0)
10031 {
10032 // Look for the vertex number in the destination
10033 // shared polyline
10034 found_vertex_index =
10035 get_connected_vertex_number_on_destination_polyline(
10036 poly_to_connect_pt,
10037 shd_bnd_left_vertex,
10038 vertex_index);
10039 } // if (poly_to_connect_pt != 0)
10040
10041 // If we have found the vertex to connect then
10042 // break the loop
10043 if (found_vertex_index)
10044 {
10045 break;
10046 } // if (found_vertex_index)
10047
10048 } // if (bound_id != new_connection_to_the_left)
10049
10050 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10051
10052 // If we have not yet found the vertex then look for it
10053 // in the sub-polylines that are not overlaped by shared
10054 // boundaries
10055 if (!found_vertex_index)
10056 {
10057 // Look for the vertex number to connect on each of
10058 // the subpolyines
10059 for (unsigned isub = 0; isub < nsub_poly; isub++)
10060 {
10061 // Only work with those sub-polylines that are not
10062 // overlaped by shared boundaries
10063 if (!boundary_marked_as_shared_boundary(
10064 uconnection_to_the_left, isub))
10065 {
10066 // Assign the pointer to the sub-polyline
10067 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10068 // Search for the vertex in the current sub-polyline
10069 found_vertex_index =
10070 get_connected_vertex_number_on_destination_polyline(
10071 poly_to_connect_pt,
10072 shd_bnd_left_vertex,
10073 vertex_index);
10074 // If we have found the vertex to connect then break the
10075 // loop
10076 if (found_vertex_index)
10077 {
10078 // But first save the subpoly number (chunk), that
10079 // will be used to perform the connection
10080 sub_poly_to_connect = isub;
10081 break;
10082 } // if (found_vertex_index)
10083
10084 } // if (not overlaped by shared boundary)
10085
10086 } // for (isub < nsub_poly)
10087
10088 } // if (!found_vertex_index)
10089
10090#ifdef PARANOID
10091 // If we could not find the vertex index to connect then
10092 // we are in trouble
10093 if (!found_vertex_index)
10094 {
10095 std::stringstream error;
10096 error
10097 << "The current shared boundary (" << bound_id << ") was "
10098 << "marked to have a connection\nto the left with the "
10099 << "boundary (" << uconnection_to_the_left << ").\n"
10100 << "This last boundary is marked to be overlaped by "
10101 << "shared boundaries\n"
10102 << "The problem is that the left vertex of the current\n"
10103 << "shared boundary is not in the list of vertices of "
10104 << "the\nboundary to connect.\n\n"
10105 << "This is the left vertex of the current shared "
10106 << "boundary\n"
10107 << "Left vertex: (" << shd_bnd_left_vertex[0] << ", "
10108 << shd_bnd_left_vertex[1] << ")\n\n"
10109 << "This is the list of vertices on the destination "
10110 << "boundary (only those subpolylines not marked as "
10111 << "overlaped by\nshared boundaries)\n";
10112 for (unsigned p = 0; p < nsub_poly; p++)
10113 {
10114 if (!boundary_marked_as_shared_boundary(
10115 uconnection_to_the_left, p))
10116 {
10117 error << "Subpolyline #(" << p << ")\n";
10118 poly_to_connect_pt = tmp_vector_subpolylines[p];
10119 const unsigned n_v = poly_to_connect_pt->nvertex();
10120 for (unsigned i = 0; i < n_v; i++)
10121 {
10122 Vector<double> cvertex =
10123 poly_to_connect_pt->vertex_coordinate(i);
10124 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10125 << cvertex[1] << ")\n";
10126 }
10127 } // Not marked as overlaped
10128 } // for (p < nsub_poly)
10129 error << "\nThis is the list of vertices of the shared "
10130 << "polylines that overlap\nthe internal "
10131 << "boundary\n";
10132 Vector<unsigned> dst_shd_bnd_ids;
10133 get_shared_boundaries_overlapping_internal_boundary(
10134 uconnection_to_the_left, dst_shd_bnd_ids);
10135 const unsigned n_shd_bnd_overlap_int_bnd =
10136 dst_shd_bnd_ids.size();
10137 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10138 {
10139 const unsigned new_connection_to_the_left =
10140 dst_shd_bnd_ids[ss];
10141 poly_to_connect_pt =
10142 boundary_polyline_pt(new_connection_to_the_left);
10143 if (poly_to_connect_pt != 0)
10144 {
10145 const unsigned shd_bnd_id_overlap =
10146 poly_to_connect_pt->boundary_id();
10147 error << "Shared boundary id(" << shd_bnd_id_overlap
10148 << ")\n";
10149 const unsigned n_v = poly_to_connect_pt->nvertex();
10150 for (unsigned i = 0; i < n_v; i++)
10151 {
10152 Vector<double> cvertex =
10153 poly_to_connect_pt->vertex_coordinate(i);
10154 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10155 << cvertex[1] << ")\n";
10156 }
10157 } // if (poly_to_connect_pt != 0)
10158 } // for (ss < n_shd_bnd_overlap_int_bnd)
10159
10160 throw OomphLibError(
10161 error.str(),
10162 "TriangleMesh::create_shared_polylines_connections()",
10163 OOMPH_EXCEPTION_LOCATION);
10164 } // if (!found_vertex_index)
10165#endif
10166
10167 // Create the connection, the left vertex of the current
10168 // shared boundary is connected with the vertex_index-th
10169 // vertex of sub_poly_to_connect-th subpolyline of the
10170 // destination boundary
10172 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10173
10174 } // else if (!connecting_to_an_overlaped_boundary)
10175
10176 } // else if (!connecting_to_an_split_boundary)
10177
10178 } // if (connection_to_the_left != -1)
10179
10180 // --------------------------------------------------------------
10181 // Connection to the right
10182 if (is_connected_to_the_right)
10183 {
10184 // Get the unsigned version of the bound id to connect to
10185 // the right
10186 const unsigned uconnection_to_the_right =
10187 shd_poly_pt->final_vertex_connected_bnd_id();
10188
10189 // The pointer to the boundary to connect
10190 TriangleMeshPolyLine* poly_to_connect_pt = 0;
10191
10192 // Flag to indicate we are trying to connect to an split
10193 // boundary
10194 bool connecting_to_an_split_boundary = false;
10195
10196 // Flag to indicate we are trying to connecto to an internal
10197 // boundary that is overlaped by a shared boundary
10198 bool connecting_to_an_overlaped_boundary = false;
10199
10200 // Check if the connection is with itself
10201 if (uconnection_to_the_right == bound_id)
10202 {
10203 // Set the pointer to the polyline to connect
10204 poly_to_connect_pt = shd_poly_pt;
10205 }
10206 else
10207 {
10208 // Get the initial shared boundary ids
10209 const unsigned initial_shd_bnd_id = initial_shared_boundary_id();
10210 // Check if the boundary to connect is a shared polyline
10211 if (uconnection_to_the_right >= initial_shd_bnd_id)
10212 {
10213 // Get the polyline pointer representing the destination
10214 // boundary
10215 poly_to_connect_pt =
10216 boundary_polyline_pt(uconnection_to_the_right);
10217 } // if (uconnection_to_the_left >= initial_shd_bnd_id)
10218 else
10219 {
10220 // If we are going to connect to an original boundary
10221 // verify if the boundary was splitted during the
10222 // distribution process to consider all the chunks
10223 // (sub-polylines) of the boundary
10224 if (boundary_was_splitted(uconnection_to_the_right))
10225 {
10226 connecting_to_an_split_boundary = true;
10227 } // if (boundary_was_splitted(uconnection_to_the_right))
10228
10229 // If we are going to connect to an original boundary
10230 // verify if the boundary, or any of its chunks is
10231 // marked to be overlapped by a shared boundary, if that
10232 // is the case we first check for connections in the
10233 // shared boundary that overlaps the internal boundary,
10234 // or the chunks, and then check for connections in the
10235 // original boundary
10236 if (connecting_to_an_split_boundary)
10237 {
10238 // Get the number of chucks that represent the
10239 // destination boundary
10240 const unsigned n_sub_poly =
10241 nboundary_subpolylines(uconnection_to_the_right);
10242 // Now loop over the chunks of the destination
10243 // boundary and if any of them is marked to be
10244 // overlaped by a shared boundary then set the flag
10245 // and break the loop
10246 for (unsigned ii = 0; ii < n_sub_poly; ii++)
10247 {
10248 if (boundary_marked_as_shared_boundary(
10249 uconnection_to_the_right, ii))
10250 {
10251 // Mark the boundary as being overlaped by a
10252 // shared boundary
10253 connecting_to_an_overlaped_boundary = true;
10254 // Break, no need to look for more overlapings
10255 break;
10256 } // if (boundary_marked_as_shared_boundary(...))
10257 } // for (ii < n_sub_poly)
10258 } // if (connecting_to_an_split_boundary)
10259 else
10260 {
10261 // If not connecting to an split boundary then check
10262 // if the whole destination boundary is overlaped by
10263 // an internal boundary
10264 if (boundary_marked_as_shared_boundary(
10265 uconnection_to_the_right, 0))
10266 {
10267 // Mark the boundary as being overlaped by a shared
10268 // boundary
10269 connecting_to_an_overlaped_boundary = true;
10270 } // if (boundary_marked_as_shared_boundary(...))
10271 } // else if (connecting_to_an_split_boundary)
10272
10273 // If we are connecting neither to an split boundary nor
10274 // an overlaped boundary then get the pointer to the
10275 // original boundary
10276 if (!(connecting_to_an_split_boundary ||
10277 connecting_to_an_overlaped_boundary))
10278 {
10279 // Get the polyline pointer representing the
10280 // destination boundary
10281 poly_to_connect_pt =
10282 boundary_polyline_pt(uconnection_to_the_right);
10283 } // else if (NOT split, NOT overlaped)
10284 } // else if (uconnection_to_the_right >= initial_shd_bnd_id)
10285
10286 } // else if (uconnection_to_the_right == bound_id)
10287
10288#ifdef PARANOID
10289 // If we are not connecting to an original boundary
10290 // (connecting to the same shared boundary or to another
10291 // shared boundary) then the boundary should not be marked
10292 // as split
10293 if (!connecting_to_an_split_boundary)
10294 {
10295 if (boundary_was_splitted(uconnection_to_the_right))
10296 {
10297 std::stringstream error;
10298 error
10299 << "The current shared boundary (" << bound_id << ") was "
10300 << "marked to have a connection\nto the right with the "
10301 << "boundary (" << uconnection_to_the_right << ").\n"
10302 << "The problem is that the destination boundary (possibly\n"
10303 << "another shared boundary) is marked to be split\n"
10304 << "There should not be split shared boundaries\n\n";
10305 throw OomphLibError(
10306 error.str(),
10307 "TriangleMesh::create_shared_polylines_connections()",
10308 OOMPH_EXCEPTION_LOCATION);
10309 }
10310 } // if (!connecting_to_an_split_boundary)
10311#endif
10312
10313 // Now look for the vertex number on the destination
10314 // boundary(ies) -- in case that the boundary was split ---
10315
10316 // Do not check for same orientation, that was previously
10317 // worked by interchanging the connections boundaries (if
10318 // necessary)
10319
10320 // Get the right vertex in the shared boundary
10321 Vector<double> shd_bnd_right_vertex =
10322 shd_poly_pt->vertex_coordinate(n_vertex - 1);
10323
10324 // If the boundary was not split then inmediately look for
10325 // the vertex index in the destination boundary
10326 if (!connecting_to_an_split_boundary)
10327 {
10328 // ... check if the boundary is marked to be overlaped by
10329 // a shared boundary
10330 if (!connecting_to_an_overlaped_boundary)
10331 {
10332 // If that is not the case then we can safely look for
10333 // the vertex number on the destination boundar
10334
10335 unsigned vertex_index = 0;
10336 const bool found_vertex_index =
10337 get_connected_vertex_number_on_destination_polyline(
10338 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10339
10340 // If we could not find the vertex index to connect then
10341 // we are in trouble
10342 if (!found_vertex_index)
10343 {
10344 std::stringstream error;
10345 error
10346 << "The current shared boundary (" << bound_id << ") was "
10347 << "marked to have a connection\nto the right with the "
10348 << "boundary (" << uconnection_to_the_right << ").\n"
10349 << "The problem is that the right vertex of the current\n"
10350 << "shared boundary is not in the list of vertices of the\n"
10351 << "boundary to connect.\n\n"
10352 << "This is the right vertex of the current shared "
10353 "boundary\n"
10354 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10355 << shd_bnd_right_vertex[1] << ")\n\n"
10356 << "This is the list of vertices on the destination "
10357 "boundary\n";
10358 const unsigned n_v = poly_to_connect_pt->nvertex();
10359 for (unsigned i = 0; i < n_v; i++)
10360 {
10361 Vector<double> cvertex =
10362 poly_to_connect_pt->vertex_coordinate(i);
10363 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10364 << cvertex[1] << ")\n";
10365 }
10366 throw OomphLibError(
10367 error.str(),
10368 "TriangleMesh::create_shared_polylines_connections()",
10369 OOMPH_EXCEPTION_LOCATION);
10370 } // if (!found_vertex_index)
10371
10372 // Create the connection, the right vertex of the current
10373 // shared boundary is connected with the vertex_index-th
10374 // vertex on the destination boundary
10376 poly_to_connect_pt, vertex_index);
10377
10378 } // if (!connecting_to_an_overlaped_boundary)
10379 else
10380 {
10381 // If the boundary is marked to be overlaped by a shared
10382 // boundary then get that shared boundary and look for
10383 // the connection in that boundary
10384
10385 // The vertex where to store the index to connect
10386 unsigned vertex_index = 0;
10387 // A flag to indicate if the connection was found
10388 bool found_vertex_index = false;
10389
10390 // Get the shared boundary id that is overlaping the
10391 // internal boundary
10392 Vector<unsigned> dst_shd_bnd_ids;
10393 get_shared_boundaries_overlapping_internal_boundary(
10394 uconnection_to_the_right, dst_shd_bnd_ids);
10395
10396 // Get the number of shared polylines that were found to
10397 // overlap the internal boundary
10398 const unsigned n_shd_bnd_overlap_int_bnd =
10399 dst_shd_bnd_ids.size();
10400
10401 // Loop over the shared boundaries that overlap the
10402 // internal boundary and look for the vertex to connect
10403 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10404 {
10405 // Get the shared polyline
10406 const unsigned new_connection_to_the_right =
10407 dst_shd_bnd_ids[ss];
10408
10409 // Get the shared polyline that is overlaping the
10410 // internal boundary
10411 poly_to_connect_pt =
10412 boundary_polyline_pt(new_connection_to_the_right);
10413
10414 if (poly_to_connect_pt != 0)
10415 {
10416 // Look for the vertex number in the destination
10417 // shared polyline
10418 found_vertex_index =
10419 get_connected_vertex_number_on_destination_polyline(
10420 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10421 } // if (poly_to_connect_pt!=0)
10422
10423 // If we have found the vertex to connect then
10424 // break the loop
10425 if (found_vertex_index)
10426 {
10427 break;
10428 } // if (found_vertex_index)
10429
10430 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10431
10432#ifdef PARANOID
10433 // If we could not find the vertex index to connect then
10434 // we are in trouble
10435 if (!found_vertex_index)
10436 {
10437 std::stringstream error;
10438 error
10439 << "The current shared boundary (" << bound_id << ") was "
10440 << "marked to have a connection\nto the right with the "
10441 << "boundary (" << uconnection_to_the_right << ").\n"
10442 << "This last boundary is marked to be overlaped by "
10443 << "shared boundaries\n"
10444 << "The problem is that the right vertex of the current\n"
10445 << "shared boundary is not in the list of vertices of the\n"
10446 << "boundary to connect.\n\n"
10447 << "This is the right vertex of the current shared "
10448 "boundary\n"
10449 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10450 << shd_bnd_right_vertex[1] << ")\n\n"
10451 << "This is the list of vertices on the destination "
10452 << "boundary\n";
10453 Vector<unsigned> dst_shd_bnd_ids;
10454 get_shared_boundaries_overlapping_internal_boundary(
10455 uconnection_to_the_right, dst_shd_bnd_ids);
10456 const unsigned n_shd_bnd_overlap_int_bnd =
10457 dst_shd_bnd_ids.size();
10458 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10459 {
10460 const unsigned new_connection_to_the_right =
10461 dst_shd_bnd_ids[ss];
10462 poly_to_connect_pt =
10463 boundary_polyline_pt(new_connection_to_the_right);
10464 if (poly_to_connect_pt != 0)
10465 {
10466 const unsigned shd_bnd_id_overlap =
10467 poly_to_connect_pt->boundary_id();
10468 error << "Shared boundary id(" << shd_bnd_id_overlap
10469 << ")\n";
10470 const unsigned n_v = poly_to_connect_pt->nvertex();
10471 for (unsigned i = 0; i < n_v; i++)
10472 {
10473 Vector<double> cvertex =
10474 poly_to_connect_pt->vertex_coordinate(i);
10475 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10476 << cvertex[1] << ")\n";
10477 }
10478 } // if (poly_to_connect_pt != 0)
10479 } // for (ss < n_shd_bnd_overlap_int_bnd)
10480
10481 throw OomphLibError(
10482 error.str(),
10483 "TriangleMesh::create_shared_polylines_connections()",
10484 OOMPH_EXCEPTION_LOCATION);
10485
10486 } // if (!found_vertex_index)
10487#endif
10488
10489 // Create the connection, the right vertex of the
10490 // current shared boundary is connected with the
10491 // vertex_index-th vertex on the destination boundary
10493 poly_to_connect_pt, vertex_index);
10494
10495 } // else if (!connecting_to_an_overlaped_boundary)
10496
10497 } // if (!connecting_to_an_split_boundary)
10498 else
10499 {
10500 // If the boundary was split then we need to look for the
10501 // vertex in the sub-polylines
10502
10503 // Get the sub-polylines vector
10504 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
10505 boundary_subpolylines(uconnection_to_the_right);
10506
10507 // Get the number of sub-polylines
10508 const unsigned nsub_poly = tmp_vector_subpolylines.size();
10509#ifdef PARANOID
10510 if (nsub_poly <= 1)
10511 {
10512 std::ostringstream error_message;
10513 error_message
10514 << "The boundary (" << uconnection_to_the_right << ") was "
10515 << "marked to be splitted but\n"
10516 << "there are only (" << nsub_poly << ") polylines to "
10517 << "represent it.\n";
10518 throw OomphLibError(
10519 error_message.str(),
10520 "TriangleMesh::create_shared_polylines_connections()",
10521 OOMPH_EXCEPTION_LOCATION);
10522 } // if (nsub_poly <= 1)
10523#endif
10524
10525 // We need to check if the boundary is marked to be
10526 // overlaped by an internal boundary, if that is the case
10527 // we need to check for each indivual subpolyline, and for
10528 // those overlaped by a shared polyline look for the
10529 // vertex in the shared polyline representation instead of
10530 // the original subpolyline
10531
10532 // ... check if the boundary is marked to be overlaped by
10533 // a shared boundary
10534 if (!connecting_to_an_overlaped_boundary)
10535 {
10536 // We can work without checking the subpolylines
10537 // individually
10538
10539 // The vertex where to store the index to connect
10540 unsigned vertex_index = 0;
10541 // The subpoly number to connect
10542 unsigned sub_poly_to_connect = 0;
10543 // A flag to indicate if the connection was found
10544 bool found_vertex_index = false;
10545
10546 // Look for the vertex number to connect on each of the
10547 // subpolyines
10548 for (unsigned isub = 0; isub < nsub_poly; isub++)
10549 {
10550 // Assign the pointer to the sub-polyline
10551 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10552 // Search for the vertex in the current sub-polyline
10553 found_vertex_index =
10554 get_connected_vertex_number_on_destination_polyline(
10555 poly_to_connect_pt, shd_bnd_right_vertex, vertex_index);
10556 // If we have found the vertex to connect then break the
10557 // loop
10558 if (found_vertex_index)
10559 {
10560 // But first save the subpoly number (chunk), that
10561 // will be used to perform the connection
10562 sub_poly_to_connect = isub;
10563 break;
10564 } // if (found_vertex_index)
10565 } // for (isub < nsub_poly)
10566
10567#ifdef PARANOID
10568 // If we could not find the vertex index to connect then
10569 // we are in trouble
10570 if (!found_vertex_index)
10571 {
10572 std::stringstream error;
10573 error
10574 << "The current shared boundary (" << bound_id << ") was "
10575 << "marked to have a connection\nto the right with the "
10576 << "boundary (" << uconnection_to_the_right << ").\n"
10577 << "The problem is that the right vertex of the current\n"
10578 << "shared boundary is not in the list of vertices of any\n"
10579 << "of the sub polylines that represent the boundary to\n"
10580 << "connect.\n\n"
10581 << "This is the right vertex of the current shared "
10582 "boundary\n"
10583 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10584 << shd_bnd_right_vertex[1] << ")\n\n"
10585 << "This is the list of vertices on the destination "
10586 << "boundary\n";
10587 for (unsigned p = 0; p < nsub_poly; p++)
10588 {
10589 error << "Subpolyline #(" << p << ")\n";
10590 poly_to_connect_pt = tmp_vector_subpolylines[p];
10591 const unsigned n_v = poly_to_connect_pt->nvertex();
10592 for (unsigned i = 0; i < n_v; i++)
10593 {
10594 Vector<double> cvertex =
10595 poly_to_connect_pt->vertex_coordinate(i);
10596 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10597 << cvertex[1] << ")\n";
10598 }
10599 } // for (p < nsub_poly)
10600 throw OomphLibError(
10601 error.str(),
10602 "TriangleMesh::create_shared_polylines_connections()",
10603 OOMPH_EXCEPTION_LOCATION);
10604 } // if (!found_vertex_index)
10605#endif
10606
10607 // Create the connection, the right vertex of the current
10608 // shared boundary is connected with the vertex_index-th
10609 // vertex of sub_poly_to_connect-th subpolyline of the
10610 // destination boundary
10612 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10613
10614 } // if (!connecting_to_an_overlaped_boundary)
10615 else
10616 {
10617 // We first look on the shared boundaries that overlap
10618 // the internal boundaries and the look for the
10619 // sub-polylines that are not marked as being overlaped
10620 // by shared boundaries
10621
10622 // The vertex where to store the index to connect
10623 unsigned vertex_index = 0;
10624 // The subpoly number to connect
10625 unsigned sub_poly_to_connect = 0;
10626 // A flag to indicate if the connection was found
10627 bool found_vertex_index = false;
10628
10629 // Get the shared boundaries id that are overlaping the
10630 // internal boundary
10631 Vector<unsigned> dst_shd_bnd_ids;
10632 get_shared_boundaries_overlapping_internal_boundary(
10633 uconnection_to_the_right, dst_shd_bnd_ids);
10634
10635 // Get the number of shared polylines that were found to
10636 // overlap the internal boundary
10637 const unsigned n_shd_bnd_overlap_int_bnd =
10638 dst_shd_bnd_ids.size();
10639
10640 // Loop over the shared boundaries that overlap the
10641 // internal boundary and look for the vertex to connect
10642 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10643 {
10644 // Get the shared polyline
10645 const unsigned new_connection_to_the_right =
10646 dst_shd_bnd_ids[ss];
10647
10648 // Make sure that the destination polyline is not the
10649 // same as the current shared polyline
10650 if (bound_id != new_connection_to_the_right)
10651 {
10652 // Get the shared polyline that is overlaping the
10653 // internal boundary
10654 poly_to_connect_pt =
10655 boundary_polyline_pt(new_connection_to_the_right);
10656
10657 if (poly_to_connect_pt != 0)
10658 {
10659 // Look for the vertex number in the destination
10660 // shared polyline
10661 found_vertex_index =
10662 get_connected_vertex_number_on_destination_polyline(
10663 poly_to_connect_pt,
10664 shd_bnd_right_vertex,
10665 vertex_index);
10666 } // if (poly_to_connect_pt != 0)
10667
10668 // If we have found the vertex to connect then
10669 // break the loop
10670 if (found_vertex_index)
10671 {
10672 break;
10673 } // if (found_vertex_index)
10674
10675 } // if (bound_id != new_connection_to_the_right)
10676
10677 } // for (ss < n_shd_bnd_overlaping_int_bnd)
10678
10679 // If we have not yet found the vertex then look for it
10680 // in the sub-polylines that are not overlaped by shared
10681 // boundaries
10682 if (!found_vertex_index)
10683 {
10684 // Look for the vertex number to connect on each of
10685 // the subpolyines
10686 for (unsigned isub = 0; isub < nsub_poly; isub++)
10687 {
10688 // Only work with those sub-polylines that are not
10689 // overlaped by shared boundaries
10690 if (!boundary_marked_as_shared_boundary(
10691 uconnection_to_the_right, isub))
10692 {
10693 // Assign the pointer to the sub-polyline
10694 poly_to_connect_pt = tmp_vector_subpolylines[isub];
10695 // Search for the vertex in the current sub-polyline
10696 found_vertex_index =
10697 get_connected_vertex_number_on_destination_polyline(
10698 poly_to_connect_pt,
10699 shd_bnd_right_vertex,
10700 vertex_index);
10701 // If we have found the vertex to connect then break the
10702 // loop
10703 if (found_vertex_index)
10704 {
10705 // But first save the subpoly number (chunk), that
10706 // will be used to perform the connection
10707 sub_poly_to_connect = isub;
10708 break;
10709 } // if (found_vertex_index)
10710
10711 } // if (not overlaped by shared boundary)
10712
10713 } // for (isub < nsub_poly)
10714
10715 } // if (!found_vertex_index)
10716
10717#ifdef PARANOID
10718 // If we could not find the vertex index to connect then
10719 // we are in trouble
10720 if (!found_vertex_index)
10721 {
10722 std::stringstream error;
10723 error
10724 << "The current shared boundary (" << bound_id << ") was "
10725 << "marked to have a connection\nto the right with the "
10726 << "boundary (" << uconnection_to_the_right << ").\n"
10727 << "This last boundary is marked to be overlaped by "
10728 << "shared boundaries\n"
10729 << "The problem is that the right vertex of the current\n"
10730 << "shared boundary is not in the list of vertices of "
10731 << "the\nboundary to connect.\n\n"
10732 << "This is the right vertex of the current shared "
10733 << "boundary\n"
10734 << "Right vertex: (" << shd_bnd_right_vertex[0] << ", "
10735 << shd_bnd_right_vertex[1] << ")\n\n"
10736 << "This is the list of vertices on the destination "
10737 << "boundary (only those subpolylines not marked as "
10738 << "overlaped by\nshared boundaries)\n";
10739 for (unsigned p = 0; p < nsub_poly; p++)
10740 {
10741 if (!boundary_marked_as_shared_boundary(
10742 uconnection_to_the_right, p))
10743 {
10744 error << "Subpolyline #(" << p << ")\n";
10745 poly_to_connect_pt = tmp_vector_subpolylines[p];
10746 const unsigned n_v = poly_to_connect_pt->nvertex();
10747 for (unsigned i = 0; i < n_v; i++)
10748 {
10749 Vector<double> cvertex =
10750 poly_to_connect_pt->vertex_coordinate(i);
10751 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10752 << cvertex[1] << ")\n";
10753 }
10754 } // Not marked as overlaped
10755 } // for (p < nsub_poly)
10756 error << "\nThis is the list of vertices of the shared "
10757 << "polylines that overlap\nthe internal "
10758 << "boundary\n";
10759 Vector<unsigned> dst_shd_bnd_ids;
10760 get_shared_boundaries_overlapping_internal_boundary(
10761 uconnection_to_the_right, dst_shd_bnd_ids);
10762 const unsigned n_shd_bnd_overlap_int_bnd =
10763 dst_shd_bnd_ids.size();
10764 for (unsigned ss = 0; ss < n_shd_bnd_overlap_int_bnd; ss++)
10765 {
10766 const unsigned new_connection_to_the_right =
10767 dst_shd_bnd_ids[ss];
10768 poly_to_connect_pt =
10769 boundary_polyline_pt(new_connection_to_the_right);
10770 if (poly_to_connect_pt != 0)
10771 {
10772 const unsigned shd_bnd_id_overlap =
10773 poly_to_connect_pt->boundary_id();
10774 error << "Shared boundary id(" << shd_bnd_id_overlap
10775 << ")\n";
10776 const unsigned n_v = poly_to_connect_pt->nvertex();
10777 for (unsigned i = 0; i < n_v; i++)
10778 {
10779 Vector<double> cvertex =
10780 poly_to_connect_pt->vertex_coordinate(i);
10781 error << "Vertex #" << i << ": (" << cvertex[0] << ", "
10782 << cvertex[1] << ")\n";
10783 }
10784 } // if (poly_to_connect_pt != 0)
10785 } // for (ss < n_shd_bnd_overlap_int_bnd)
10786
10787 throw OomphLibError(
10788 error.str(),
10789 "TriangleMesh::create_shared_polylines_connections()",
10790 OOMPH_EXCEPTION_LOCATION);
10791 } // if (!found_vertex_index)
10792#endif
10793
10794 // Create the connection, the left vertex of the current
10795 // shared boundary is connected with the vertex_index-th
10796 // vertex of sub_poly_to_connect-th subpolyline of the
10797 // destination boundary
10799 poly_to_connect_pt, vertex_index, sub_poly_to_connect);
10800
10801 } // else if (!connecting_to_an_overlaped_boundary)
10802
10803 } // else if (!connecting_to_an_split_boundary)
10804
10805 } // if (connection_to_the_right != -1)
10806
10807 } // if (connection_to_the_left != -1 || connection_to_the_right != -1)
10808
10809 } // for (ipoly < npoly)
10810
10811 } // for (icurve < ncurves)
10812 }
10813
10814 //=======================================================================
10815 // Compute the holes left by the halo elements, those adjacent
10816 // to the shared boundaries
10817 //=======================================================================
10818 template<class ELEMENT>
10820 Vector<Vector<double>>& output_holes_coordinates)
10821 {
10822 // Storage for number of processors and current processor
10823 const unsigned n_proc = this->communicator_pt()->nproc();
10824 const unsigned my_rank = this->communicator_pt()->my_rank();
10825
10826 // Mark those done elements, so we do not repeat any coordinate left
10827 // by repeated halo elements
10828 std::map<FiniteElement*, bool> done_ele;
10829
10830 // Loop over the processors and get the shared boundaries ids that
10831 // the current processor has with the other processors
10832 for (unsigned iproc = 0; iproc < n_proc; iproc++)
10833 {
10834 // There are shared boundaries only with the other processors
10835 if (iproc != my_rank)
10836 {
10837 // Get the number of shared boundaries with the iproc
10838 const unsigned n_shd_bnd_iproc = nshared_boundaries(my_rank, iproc);
10839
10840#ifdef PARANOID
10841 // Get the number of shared boundaries with the iproc, but
10842 // reversing the indexes
10843 const unsigned n_shd_bnd_iproc_rev = nshared_boundaries(iproc, my_rank);
10844 if (n_shd_bnd_iproc != n_shd_bnd_iproc_rev)
10845 {
10846 std::ostringstream error_stream;
10847 error_stream
10848 << "The number of shared boundaries of processor (" << my_rank
10849 << ") with processor(" << iproc << "): (" << n_shd_bnd_iproc
10850 << ")\n"
10851 << "is different from the number of shared boundaries of "
10852 << "processor (" << iproc << ")\nwith processor (" << my_rank
10853 << "): (" << n_shd_bnd_iproc << ")\n\n";
10854 throw OomphLibError(error_stream.str(),
10855 OOMPH_CURRENT_FUNCTION,
10856 OOMPH_EXCEPTION_LOCATION);
10857
10858 } // if (n_shd_bnd_iproc != n_shd_bnd_iproc_rev)
10859#endif
10860
10861 // Loop over the shared boundaries ids
10862 for (unsigned i = 0; i < n_shd_bnd_iproc; i++)
10863 {
10864 // Get the shared boundary id
10865 const unsigned shd_bnd_id = shared_boundaries_ids(my_rank, iproc, i);
10866
10867 // Get the number of shared boundary elements
10868 const unsigned n_shd_bnd_ele = nshared_boundary_element(shd_bnd_id);
10869
10870 // Loop over the shared boundary elements
10871 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
10872 {
10873 // Get the shared boundary element
10874 FiniteElement* ele_pt = shared_boundary_element_pt(shd_bnd_id, e);
10875
10876 // Only work with halo elements
10877 if (ele_pt->is_halo())
10878 {
10879 // If the element has not been visited
10880 if (!done_ele[ele_pt])
10881 {
10882 // Get the number of nodes
10883 const unsigned n_nodes = ele_pt->nnode();
10884
10885 // Compute the centroid of the element
10886 Vector<double> element_centroid(2, 0.0);
10887 // Loop over the nodes
10888 for (unsigned k = 0; k < n_nodes; k++)
10889 {
10890 Node* tmp_node_pt = ele_pt->node_pt(k);
10891 // Loop over the dimension
10892 for (unsigned d = 0; d < 2; d++)
10893 {
10894 element_centroid[d] += tmp_node_pt->x(d);
10895 } // for (d < 2)
10896 } // for (k < n_nodes)
10897
10898 // Average the data
10899 for (unsigned d = 0; d < 2; d++)
10900 {
10901 element_centroid[d] = element_centroid[d] / (double)n_nodes;
10902 } // for (d < 2)
10903
10904 // Add the centroid to the output holes
10905 output_holes_coordinates.push_back(element_centroid);
10906
10907 } // if (!done_ele[ele_pt])
10908
10909 } // if (ele_pt->is_halo())
10910
10911 } // for1 (e < n_shd_bnd_ele)
10912
10913 } // for (i < n_shd_bnd_iproc)
10914
10915 } // if (iproc != my_rank)
10916
10917 } // for (iproc < n_proc)
10918 }
10919
10920 //======================================================================
10921 // Keeps those vertices that define a hole, those that are
10922 // inside closed internal boundaries in the new polygons that define
10923 // the domain. Delete those outside/inside the outer polygons (this
10924 // is required since Triangle can not deal with vertices that define
10925 // holes outside the new outer polygons of the domain)
10926 //======================================================================
10927 template<class ELEMENT>
10929 Vector<TriangleMeshPolygon*>& polygons_pt,
10930 Vector<Vector<double>>& output_holes_coordinates)
10931 {
10932 // General strategy
10933
10934 // 1) Identify the inner closed boundaries
10935
10936 // 2) Separate the vertices in three groups
10937
10938 // --- 2.1) The vertices inside the inner closed boundaries, these
10939 // are not deleted because they define holes
10940
10941 // --- 2.2) The vertices outside the outer boundaries, these are
10942 // deleted only if they are outside the convex hull defined
10943 // by all the polygons
10944
10945 // --- 2.3) Any other vertex is deleted
10946
10947 // Get the number of input holes
10948 const unsigned n_input_holes = output_holes_coordinates.size();
10949
10950 // Only do something if there are holes
10951 if (n_input_holes == 0)
10952 {
10953 return;
10954 }
10955
10956 // Get the number of input polygons
10957 const unsigned n_polygons = polygons_pt.size();
10958
10959 // Store the vertices of all the input polygons
10960 // vertices_polygons[x][ ][ ]: Polygon number
10961 // vertices_polygons[ ][x][ ]: Vertex number
10962 // vertices_polygons[ ][ ][x]: Vertex coordinate
10963 Vector<Vector<Vector<double>>> vertices_polygons(n_polygons);
10964
10965 // Loop over all the polygons and get the vertices
10966 for (unsigned p = 0; p < n_polygons; p++)
10967 {
10968 // Get the number of polylines associated to the polygon
10969 const unsigned n_polylines = polygons_pt[p]->npolyline();
10970 // Loop over the polylines and get the vertices
10971 for (unsigned pp = 0; pp < n_polylines; pp++)
10972 {
10973 // Get the polyline
10974 const TriangleMeshPolyLine* tmp_poly_pt =
10975 polygons_pt[p]->polyline_pt(pp);
10976 // Get the number of vertices in the polyline
10977 const unsigned n_vertices = tmp_poly_pt->nvertex();
10978 // Loop over the vertices but only add (n_vertices-1) vertices,
10979 // the last vertex of polyline (pp) is the first vertex of
10980 // polyline (pp+1)
10981 for (unsigned v = 0; v < n_vertices - 1; v++)
10982 {
10983 // Get the current vertex
10984 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
10985 vertices_polygons[p].push_back(current_vertex);
10986 } // for (v < nvertex)
10987 } // for (p < nouter_polylines)
10988 } // for (p < n_polygons)
10989
10990 // -------------------------------------------------------------------
10991 // 1) Identify the inner closed boundaries
10992 // -------------------------------------------------------------------
10993
10994 // A container that indicates if a given polygon should be
10995 // considered as an outer or as an inner polygon. By default all the
10996 // polygons are considered as outer polygons
10997 std::vector<bool> is_outer_polygon(n_polygons, true);
10998
10999 // We only check for innner polygons if there are more than one
11000 // polygon
11001 if (n_polygons > 1)
11002 {
11003 // Propose an inner polygon, if one of the middle points of its
11004 // edges lies inside any other polygon then the proposed inner
11005 // polygon is marked as an internal polygon
11006
11007 // Pre-compute the middle points of the edges in the polygons
11008 Vector<Vector<Vector<double>>> polygon_edge_middle_vertex(n_polygons);
11009
11010 for (unsigned p = 0; p < n_polygons; p++)
11011 {
11012 // Temporary store the vertices of the proposed inner polygon
11013 Vector<Vector<double>> tmp_inner_polygon = vertices_polygons[p];
11014
11015 // Get the number of vertices in the current proposed inner polygon
11016 const unsigned n_vertices = tmp_inner_polygon.size();
11017
11018 // Resize with the number of edges in the polygon
11019 polygon_edge_middle_vertex[p].resize(n_vertices - 1);
11020
11021 // Loop over the vertices and compute the middle point in the edge
11022 // that joins each pair of contiguous vertices
11023 for (unsigned e = 0; e < n_vertices - 1; e++)
11024 {
11025 // The dimension
11026 const unsigned dim = 2;
11027 polygon_edge_middle_vertex[p][e].resize(dim);
11028 for (unsigned d = 0; d < dim; d++)
11029 {
11030 polygon_edge_middle_vertex[p][e][d] =
11031 (tmp_inner_polygon[e][d] + tmp_inner_polygon[e + 1][d]) / 2.0;
11032 } // for (d < 2)
11033
11034 } // for (e < n_vertices - 1)
11035
11036 } // for (p < n_polygons)
11037
11038 // Loop over the polygons and for every loop propose a different
11039 // inner polygon
11040 for (unsigned idx_inner = 0; idx_inner < n_polygons; idx_inner++)
11041 {
11042 // Flag to indicate that ONE of the middle edge vertices of the
11043 // proposed inner polygon is inside another polygon, this will
11044 // set the proposed inner polygon as an actual inner polygon
11045 bool is_inner_polygon = false;
11046
11047 // Loop over all the polygons, except the proposed one and check
11048 // if all the middle edges of its edges are inside any other
11049 // polygon
11050 for (unsigned i = 0; i < n_polygons; i++)
11051 {
11052 // Do not check with the polygon itself
11053 if (i != idx_inner)
11054 {
11055 // Get the number of edges of the proposed inner polygon
11056 const unsigned n_edges =
11057 polygon_edge_middle_vertex[idx_inner].size();
11058 // Loop over the middle points in the edges of the current
11059 // proposed inner polygon
11060 for (unsigned e = 0; e < n_edges; e++)
11061 {
11062 // Get the vertex in the current proposed inner polygon
11063 Vector<double> current_vertex =
11064 polygon_edge_middle_vertex[idx_inner][e];
11065 // Check if the current vertex is inside the current i-th
11066 // polygon
11067 const bool is_point_inside = is_point_inside_polygon_helper(
11068 vertices_polygons[i], current_vertex);
11069
11070 // If one point is inside then the polygon is inside the
11071 // i-th polygon
11072 if (is_point_inside)
11073 {
11074 // The polygon is an inner polygon
11075 is_inner_polygon = true;
11076 // Break the loop
11077 break;
11078 } // if (is_point_inside)
11079
11080 } // for (e < n_edges)
11081
11082 } // if (i != idx_inner)
11083
11084 // Are all the vertices of the current proposed inner polygon
11085 // inside the i-th polygon
11086 if (is_inner_polygon)
11087 {
11088 // The current proposed inner polygon is an actual inner
11089 // polygon, and is inside the i-th polygon
11090 break;
11091 }
11092
11093 } // for (i < n_polygons)
11094
11095 // Is the current proposed inner polygon an actual inner polygon
11096 if (is_inner_polygon)
11097 {
11098 // The current proposed inner polygon is a real inner polygon
11099 is_outer_polygon[idx_inner] = false;
11100 }
11101 else
11102 {
11103 // The current proposed inner polygon IS NOT a real inner
11104 // polygon
11105 is_outer_polygon[idx_inner] = true;
11106 }
11107
11108 } // for (idx_outer < npolygons)
11109
11110 } // if (n_polygons > 1)
11111
11112 // Count the number of outer closed boundaries and inner closed
11113 // boundaries
11114 unsigned n_outer_polygons = 0;
11115 unsigned n_inner_polygons = 0;
11116 // Also get the indexes of the inner polygons
11117 Vector<unsigned> index_inner_polygon;
11118 // Loop over the polygons
11119 for (unsigned i = 0; i < n_polygons; i++)
11120 {
11121 if (is_outer_polygon[i])
11122 {
11123 // Increase the counter for outer polygons
11124 n_outer_polygons++;
11125 }
11126 else
11127 {
11128 // Increase the counter for inner polygons
11129 n_inner_polygons++;
11130 // Store the index of the inner polygon
11131 index_inner_polygon.push_back(i);
11132 }
11133 } // for (i < n_polygons)
11134
11135 // -------------------------------------------------------------------
11136 // 2) Separate the vertices in three groups
11137
11138 // --- 2.1) The vertices inside the inner closed boundaries, these are
11139 // not deleted because they define holes
11140
11141 // --- 2.2) The vertices outside the outer boundaries, these are
11142 // deleted only if they are outside the convex hull defined
11143 // by all the polygons
11144
11145 // --- 2.3) Any other vertex is deleted
11146 // -------------------------------------------------------------------
11147
11148 // Keep track of the vertices inside the inner closed boundaries (by
11149 // default all vertices not inside the inner polygons)
11150 std::vector<bool> is_inside_an_inner_polygon(n_input_holes, false);
11151
11152 // Keep track of the vertices outside the outer closed boundaries
11153 // (by default all the vertices are outside the outer polygons)
11154 std::vector<bool> is_outside_the_outer_polygons(n_input_holes, true);
11155
11156 // Keep track of the vertices inside the convex hull (by default
11157 // all the vertices are not inside the convex hull)
11158 std::vector<bool> is_inside_the_convex_hull(n_input_holes, false);
11159
11160 // Mark the vertices inside the inner closed boundaries
11161 Vector<Vector<Vector<double>>> vertex_inside_inner_polygon(
11162 n_inner_polygons);
11163
11164 // -------------------------------------------------------------------
11165 // Loop over the inner polygons and find all the vertices inside
11166 // each one
11167 for (unsigned i = 0; i < n_inner_polygons; i++)
11168 {
11169 // Get the vertex of the inner polygon
11170 const unsigned ii = index_inner_polygon[i];
11171 // Loop over the vertices defining holes, mark and store those
11172 // inside the inner polygon
11173 for (unsigned h = 0; h < n_input_holes; h++)
11174 {
11175 // Check if the vertex has not been already marked as inside
11176 // another polygon
11177 if (!is_inside_an_inner_polygon[h])
11178 {
11179 // Check if the hole is inside the current inner polygon
11180 const bool is_inside_polygon = is_point_inside_polygon_helper(
11181 vertices_polygons[ii], output_holes_coordinates[h]);
11182
11183 // If the vertex is inside the current inner polygon then mark
11184 // it and associate the vertices to the current inner polygon
11185 if (is_inside_polygon)
11186 {
11187 // Set as inside an inner polygon
11188 is_inside_an_inner_polygon[h] = true;
11189 // Associate the vertex to the current inner polygon
11190 vertex_inside_inner_polygon[i].push_back(
11191 output_holes_coordinates[h]);
11192 } // if (is_inside_polygon)
11193
11194 } // if (!is_inside_an_inner_polygon[h])
11195
11196 } // for (h < n_input_holes)
11197
11198 } // for (i < n_polygons)
11199
11200 // -------------------------------------------------------------------
11201 // Loop over the vertices defining holes and mark those as outside the
11202 // outer polygons
11203 for (unsigned h = 0; h < n_input_holes; h++)
11204 {
11205 // Check if the vertex has not been already marked as inside
11206 // another polygon
11207 if (!is_inside_an_inner_polygon[h])
11208 {
11209 // Loop over the polygons and check if the vertex is outside ALL
11210 // the outer polygons
11211 for (unsigned i = 0; i < n_polygons; i++)
11212 {
11213 // Only work with outer polygons
11214 if (is_outer_polygon[i])
11215 {
11216 // Check if the hole is inside the current outer polygon
11217 const bool is_inside_polygon = is_point_inside_polygon_helper(
11218 vertices_polygons[i], output_holes_coordinates[h]);
11219
11220 // If the vertex is inside the current outer polygon then
11221 // mark it and break the loop (it is not outside ALL the
11222 // polygons)
11223 if (is_inside_polygon)
11224 {
11225 // Set as inside an outer polygon
11226 is_outside_the_outer_polygons[h] = false;
11227 // Break the loop
11228 break;
11229 } // if (is_inside_polygon)
11230
11231 } // if (is_outer_polygon[i])
11232
11233 } // for (i < n_polygons)
11234
11235 } // if (!is_inside_an_inner_polygon[h])
11236 else
11237 {
11238 // If the vertex is inside an inner polygon then it is inside an
11239 // outer polygon
11240 is_outside_the_outer_polygons[h] = false;
11241 } // else if (!is_inside_an_inner_polygon[h])
11242
11243 } // for (h < n_input_holes)
11244
11245 // -------------------------------------------------------------------
11246 // Compute the convex hull Create the data structure
11247 std::vector<Point> input_vertices_convex_hull;
11248 // Copy ALL the vertices of the polygons
11249 // Loop over the polygons
11250 for (unsigned p = 0; p < n_polygons; p++)
11251 {
11252 // Get the number of vertices
11253 const unsigned n_vertices = vertices_polygons[p].size();
11254 // Loop over the vertices in the polygon
11255 for (unsigned v = 0; v < n_vertices; v++)
11256 {
11257 // Create a new "Point" to store in the input vertices
11258 Point point;
11259 // Assign the values to the "Point"
11260 point.x = vertices_polygons[p][v][0];
11261 point.y = vertices_polygons[p][v][1];
11262 // Add the "Point" to the input vertices
11263 input_vertices_convex_hull.push_back(point);
11264 } // for (v < n_vertices)
11265 } // for (p < n_polygons)
11266
11267 // Compute the convex hull
11268 std::vector<Point> output_vertices_convex_hull =
11269 convex_hull(input_vertices_convex_hull);
11270
11271 // Get the number of vertices in the convex hull
11272 const unsigned n_vertices_convex_hull = output_vertices_convex_hull.size();
11273
11274 // Copy the output to the used data structures
11275 Vector<Vector<double>> vertices_convex_hull(n_vertices_convex_hull);
11276 for (unsigned i = 0; i < n_vertices_convex_hull; i++)
11277 {
11278 // Resize the data structure
11279 vertices_convex_hull[i].resize(2);
11280 // Copy the data
11281 vertices_convex_hull[i][0] = output_vertices_convex_hull[i].x;
11282 vertices_convex_hull[i][1] = output_vertices_convex_hull[i].y;
11283 } // for (i < n_vertices_convex_hull)
11284
11285 // Loop over the vertices defining holes, work only with those
11286 // outside ALL the outer boundaries and mark those inside the convex
11287 // hull
11288 for (unsigned h = 0; h < n_input_holes; h++)
11289 {
11290 // Only work with those outside ALL the outer polygons
11291 if (is_outside_the_outer_polygons[h])
11292 {
11293 // Check if the hole is inside the convex hull
11294 const bool is_inside_convex_hull = is_point_inside_polygon_helper(
11295 vertices_convex_hull, output_holes_coordinates[h]);
11296
11297 // If the vertex is inside the convex hull then mark it
11298 if (is_inside_convex_hull)
11299 {
11300 // Set as inside the convex hull
11301 is_inside_the_convex_hull[h] = true;
11302 } // if (is_inside_convex_hull)
11303
11304 } // if (is_outside_the_outer_polygons[h])
11305 else
11306 {
11307 // Any vertex inside any outer polygon is inside the convex hull
11308 is_inside_the_convex_hull[h] = true;
11309 } // else if (is_outside_the_outer_polygons[h])
11310
11311 } // for (h < n_input_holes)
11312
11313 // Store the output holes, only (those inside an inner polygon) OR
11314 // (those outside ALL the polygons AND inside the convex hull)
11315 Vector<Vector<double>> hole_kept;
11316 for (unsigned h = 0; h < n_input_holes; h++)
11317 {
11318 // Check if the hole should be kept
11319 if ((is_inside_an_inner_polygon[h]) ||
11320 (is_outside_the_outer_polygons[h] && is_inside_the_convex_hull[h]))
11321 {
11322 // Copy the hole information
11323 hole_kept.push_back(output_holes_coordinates[h]);
11324 } // if (keep_hole[h])
11325 } // for (h < n_input_holes)
11326
11327 // Clear the previous storage
11328 output_holes_coordinates.clear();
11329 // Set the output holes
11330 output_holes_coordinates = hole_kept;
11331 }
11332
11333 //======================================================================
11334 // Sorts the polylines so they be contiguous and then we can
11335 // create a closed or open curve from them
11336 //======================================================================
11337 template<class ELEMENT>
11339 Vector<TriangleMeshPolyLine*>& unsorted_polylines_pt,
11340 Vector<Vector<TriangleMeshPolyLine*>>& sorted_polylines_pt)
11341 {
11342 unsigned n_unsorted_polylines = unsorted_polylines_pt.size();
11343 unsigned n_sorted_polylines = 0;
11344 unsigned curves_index = 0;
11345
11346 // Map to know which polyline has been already sorted
11347 std::map<TriangleMeshPolyLine*, bool> done_polyline;
11348
11349 do
11350 {
11351 // Create the list that stores the polylines and allows to introduce
11352 // polylines to the left and to the right
11353 std::list<TriangleMeshPolyLine*> sorted_polyline_list_pt;
11354 bool changes = false;
11355
11356 // Create pointers to the left and right "side" of the sorted list of
11357 // new created TriangleMeshPolyLines
11358 TriangleMeshPolyLine* left_pt = 0;
11359 TriangleMeshPolyLine* right_pt = 0;
11360
11361 // 1) Take the first non done polyline on the unsorted list of polylines
11362 unsigned pp = 0;
11363 bool found_root_polyline = false;
11364 while (pp < n_unsorted_polylines && !found_root_polyline)
11365 {
11366 if (!done_polyline[unsorted_polylines_pt[pp]])
11367 {
11368 found_root_polyline = true;
11369 }
11370 else
11371 {
11372 pp++;
11373 }
11374 }
11375
11376 // Check if there are polylines to be sorted
11377 if (pp < n_unsorted_polylines)
11378 {
11379 // 2) Mark the polyline as done
11380 left_pt = right_pt = unsorted_polylines_pt[pp];
11381 done_polyline[left_pt] = true;
11382 // Increment the number of sorted polylines
11383 n_sorted_polylines++;
11384
11385 // 3) Add this polyline to the sorted list and use it as root
11386 // to sort the other polylines
11387 sorted_polyline_list_pt.push_back(left_pt);
11388
11389 do
11390 {
11391 changes = false;
11392
11393 Vector<double> left_vertex(2);
11394 Vector<double> right_vertex(2);
11395
11396 left_pt->initial_vertex_coordinate(left_vertex);
11397 right_pt->final_vertex_coordinate(right_vertex);
11398
11399 for (unsigned i = pp + 1; i < n_unsorted_polylines; i++)
11400 {
11401 TriangleMeshPolyLine* current_polyline_pt =
11402 unsorted_polylines_pt[i];
11403 if (!done_polyline[current_polyline_pt])
11404 {
11405 Vector<double> initial_vertex(2);
11406 Vector<double> final_vertex(2);
11407 current_polyline_pt->initial_vertex_coordinate(initial_vertex);
11408 current_polyline_pt->final_vertex_coordinate(final_vertex);
11409
11410 // Compare if the current polyline should go to the left or
11411 // to the right on the sorted polyline list
11412
11413 // Go to the left
11414 if (left_vertex == final_vertex)
11415 {
11416 left_pt = current_polyline_pt;
11417 sorted_polyline_list_pt.push_front(left_pt);
11418 done_polyline[left_pt] = true;
11419 n_sorted_polylines++;
11420
11421 // We have added one more polyline, go for another round
11422 changes = true;
11423 }
11424 // Go to the right
11425 else if (right_vertex == initial_vertex)
11426 {
11427 right_pt = current_polyline_pt;
11428 sorted_polyline_list_pt.push_back(right_pt);
11429 done_polyline[right_pt] = true;
11430 n_sorted_polylines++;
11431
11432 // We have added one more polyline, go for another round
11433 changes = true;
11434 }
11435 // Go to the left but it is reversed
11436 else if (left_vertex == initial_vertex)
11437 {
11438 current_polyline_pt->reverse();
11439 left_pt = current_polyline_pt;
11440 sorted_polyline_list_pt.push_front(left_pt);
11441 done_polyline[left_pt] = true;
11442 n_sorted_polylines++;
11443
11444 // We have added one more polyline, go for another round
11445 changes = true;
11446 }
11447 // Go to the right but it is reversed
11448 else if (right_vertex == final_vertex)
11449 {
11450 current_polyline_pt->reverse();
11451 right_pt = current_polyline_pt;
11452 sorted_polyline_list_pt.push_back(right_pt);
11453 done_polyline[right_pt] = true;
11454 n_sorted_polylines++;
11455
11456 // We have added one more polyline, go for another round
11457 changes = true;
11458 }
11459 } // if (!done_polyline[current_polyline_pt])
11460 if (changes)
11461 {
11462 break;
11463 }
11464 } // for (i < n_unsorted_polylines)
11465 } while (changes);
11466
11467 } // if (pp < n_unsorted_polylines)
11468 else
11469 {
11470 // All the polylines are now on the sorted list of polylines
11471#ifdef PARANOID
11472 // This case comes when it was not possible to find a root polyline
11473 // since all of them are marked as done but the number of sorted and
11474 // unsorted polylines is not the same
11475 if (!found_root_polyline)
11476 {
11477 std::stringstream err;
11478 err << "It was not possible to find a root polyline to sort the "
11479 << "others around it.\nThe number of unsorted and sorted "
11480 << "polylines is different, it means that\nnot all the "
11481 << "polylines have been sorted.\n"
11482 << "Found root polyline: (" << found_root_polyline << ")\n"
11483 << "Sorted polylines: (" << n_sorted_polylines << ")\n"
11484 << "Unsorted polylines: (" << n_unsorted_polylines << ")\n";
11485 throw OomphLibError(err.str(),
11486 "TriangleMesh::sort_polylines_helper()",
11487 OOMPH_EXCEPTION_LOCATION);
11488 }
11489#endif
11490 }
11491
11492 // Create the storage for the new sorted polylines and copy them on the
11493 // vector structure for sorted polylines
11494 unsigned n_sorted_polyline_on_list = sorted_polyline_list_pt.size();
11495
11496 // Create the temporal vector that stores the sorted polylines
11497 Vector<TriangleMeshPolyLine*> tmp_sorted_polylines(
11498 n_sorted_polyline_on_list);
11499 unsigned counter = 0;
11500
11501 std::list<TriangleMeshPolyLine*>::iterator it_polyline;
11502 for (it_polyline = sorted_polyline_list_pt.begin();
11503 it_polyline != sorted_polyline_list_pt.end();
11504 it_polyline++)
11505 {
11506 tmp_sorted_polylines[counter] = *it_polyline;
11507 counter++;
11508 }
11509
11510 sorted_polylines_pt.push_back(tmp_sorted_polylines);
11511
11512 ++curves_index;
11513
11514 } while (n_sorted_polylines < n_unsorted_polylines);
11515
11516#ifdef PARANOID
11517 // Verify that the number of polylines on the sorted list is the same
11518 // as the number of polylines on the unsorted list
11519 if (n_sorted_polylines != n_unsorted_polylines)
11520 {
11521 std::stringstream err;
11522 err << "The number of polylines on the unsorted and sorted vectors"
11523 << " is different,\n"
11524 << "it means that not all the polylines have been sorted.\n"
11525 << "Sorted polylines: " << n_sorted_polylines
11526 << "\nUnsorted polylines: " << n_unsorted_polylines;
11527 throw OomphLibError(err.str(),
11528 "TriangleMesh::sort_polylines_helper()",
11529 OOMPH_EXCEPTION_LOCATION);
11530 }
11531#endif
11532 }
11533
11534 //======================================================================
11535 // Creates the shared boundaries
11536 //======================================================================
11537 template<class ELEMENT>
11539 OomphCommunicator* comm_pt,
11540 const Vector<unsigned>& element_domain,
11541 const Vector<GeneralisedElement*>& backed_up_el_pt,
11542 const Vector<FiniteElement*>& backed_up_f_el_pt,
11543 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11544 const bool& overrule_keep_as_halo_element_status)
11545 {
11546 // Storage for number of processors and current processor
11547 const unsigned nproc = comm_pt->nproc();
11548 const unsigned my_rank = comm_pt->my_rank();
11549
11550 // Storage for all the halo elements on all processors
11551 // halo_element[iproc][jproc][ele_number]
11552 // Stores the "ele_number"-th halo element of processor "iproc" with
11553 // processor "jproc"
11554 Vector<Vector<Vector<GeneralisedElement*>>> halo_element_pt(nproc);
11555 // Create complete storage for the halo_element_pt container
11556 for (unsigned iproc = 0; iproc < nproc; iproc++)
11557 {
11558 halo_element_pt[iproc].resize(nproc);
11559 }
11560
11561 // Store the global index of the element, used to check for possible
11562 // misclassification of halo elements in the above container
11563 // (halo_element_pt)
11564 std::map<GeneralisedElement*, unsigned> element_to_global_index;
11565
11566 // Get the halo elements on all processors
11567 this->get_halo_elements_on_all_procs(nproc,
11568 element_domain,
11569 backed_up_el_pt,
11570 processors_associated_with_data,
11571 overrule_keep_as_halo_element_status,
11572 element_to_global_index,
11573 halo_element_pt);
11574
11575 // Resize the shared polylines container
11576 flush_shared_boundary_polyline_pt();
11577 Shared_boundary_polyline_pt.resize(nproc);
11578
11579 // Create a set that store only the elements that will be kept in
11580 // the processor as nonhalo element, those whose element_domains is
11581 // equal to my_rank. This set is used when creating the shared
11582 // polylines and identify the connections to the original boundaries
11583 std::set<FiniteElement*> element_in_processor_pt;
11584 const unsigned n_ele = backed_up_f_el_pt.size();
11585 for (unsigned e = 0; e < n_ele; e++)
11586 {
11587 if (element_domain[e] == my_rank)
11588 {
11589 element_in_processor_pt.insert(backed_up_f_el_pt[e]);
11590 } // if (element_domain[e] == my_rank)
11591 } // for (e < n_elex)
11592
11593 // Look for elements edges that may lie on internal boundaries
11594 // If that is the case then relate the face with the boundary on
11595 // which it lies
11596 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
11597 this->get_element_edges_on_boundary(elements_edges_on_boundary);
11598
11599 // Now we have all the halo elements on all processors. Use the
11600 // edges shared by the halo elements to create the shared boundaries.
11601 this->create_polylines_from_halo_elements_helper(
11602 element_domain,
11603 element_to_global_index,
11604 element_in_processor_pt,
11605 halo_element_pt,
11606 elements_edges_on_boundary,
11607 Shared_boundary_polyline_pt);
11608 }
11609
11610 //======================================================================
11611 /// Creates the halo elements on all processors
11612 /// Gets the halo elements on all processors, these elements are then used
11613 /// on the function that computes the shared boundaries among the processors
11614 //======================================================================
11615 template<class ELEMENT>
11617 const unsigned& nproc,
11618 const Vector<unsigned>& element_domain,
11619 const Vector<GeneralisedElement*>& backed_up_el_pt,
11620 std::map<Data*, std::set<unsigned>>& processors_associated_with_data,
11621 const bool& overrule_keep_as_halo_element_status,
11622 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11623 Vector<Vector<Vector<GeneralisedElement*>>>& output_halo_elements_pt)
11624 {
11625 const unsigned n_ele = backed_up_el_pt.size();
11626
11627 // Loop over all the processors
11628 for (unsigned iproc = 0; iproc < nproc; iproc++)
11629 {
11630 // Boolean to know which elements has been already added to the
11631 // halo scheme on "iproc" processor
11632 Vector<std::map<GeneralisedElement*, bool>> already_added(nproc);
11633
11634 // Loop over all backed up elements
11635 for (unsigned e = 0; e < n_ele; e++)
11636 {
11637 // Get element and its domain
11638 GeneralisedElement* el_pt = backed_up_el_pt[e];
11639 unsigned el_domain = element_domain[e];
11640
11641 // If element is NOT located on "iproc" processor then check if it is
11642 // halo with "el_domain" processor
11643 if (el_domain != iproc)
11644 {
11645 // If this current mesh has been told to keep all elements as halos,
11646 // OR the element itself knows that it must be kept then
11647 // keep it
11648 if ((this->Keep_all_elements_as_halos) ||
11649 (el_pt->must_be_kept_as_halo()))
11650 {
11651 if (!overrule_keep_as_halo_element_status)
11652 {
11653 // Add as halo element whose non-halo counterpart is
11654 // located on processor "el_domain"
11655 if (!already_added[el_domain][el_pt])
11656 {
11657 output_halo_elements_pt[iproc][el_domain].push_back(el_pt);
11658 already_added[el_domain][el_pt] = true;
11659 element_to_global_index[el_pt] = e;
11660 }
11661 }
11662 }
11663 // Otherwise: Is one of the nodes associated with other processor?
11664 else
11665 {
11666 // Can only have nodes if this is a finite element
11667 FiniteElement* finite_el_pt = dynamic_cast<FiniteElement*>(el_pt);
11668 if (finite_el_pt != 0)
11669 {
11670 unsigned n_node = finite_el_pt->nnode();
11671 for (unsigned n = 0; n < n_node; n++)
11672 {
11673 Node* nod_pt = finite_el_pt->node_pt(n);
11674
11675 // Keep element?
11676 std::set<unsigned>::iterator it =
11677 processors_associated_with_data[nod_pt].find(iproc);
11678 if (it != processors_associated_with_data[nod_pt].end())
11679 {
11680 // Add as root halo element whose non-halo counterpart is
11681 // located on processor "el_domain"
11682 if (!already_added[el_domain][el_pt])
11683 {
11684 output_halo_elements_pt[iproc][el_domain].push_back(el_pt);
11685 already_added[el_domain][el_pt] = true;
11686 element_to_global_index[el_pt] = e;
11687 }
11688 // Now break out of loop over nodes
11689 break;
11690 } // if (it!=processors_associated_with_data[nod_pt].end())
11691 } // for (n < n_node)
11692 } // if (finite_el_pt!=0)
11693 } // else (this->Keep_all_elements_as_halos)
11694 } // if (el_domain!=iproc)
11695 } // for (e < nele)
11696 } // for (iproc < nproc)
11697 }
11698
11699 //====================================================================
11700 // Get the element edges (pair of nodes, edges) that lie
11701 // on a boundary (used to mark shared boundaries that lie on
11702 // internal boundaries)
11703 //====================================================================
11704 template<class ELEMENT>
11706 std::map<std::pair<Node*, Node*>, unsigned>& element_edges_on_boundary)
11707 {
11708 // The number of original boundaries
11709 const unsigned nbound = this->nboundary();
11710 // Loop over the boundaries
11711 for (unsigned b = 0; b < nbound; b++)
11712 {
11713 // Keep track of the pair of nodes done
11714 std::map<std::pair<Node*, Node*>, bool> edge_done;
11715 // Get the number of elements on the boundary
11716 const unsigned nbound_ele = this->nboundary_element(b);
11717 for (unsigned e = 0; e < nbound_ele; e++)
11718 {
11719 // Get the boundary bulk element
11720 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
11721 // Get the face index
11722 int face_index = this->face_index_at_boundary(b, e);
11723 // Create the face element
11724 FiniteElement* face_ele_pt =
11725 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
11726 // Get the number of nodes on the face element
11727 const unsigned nnodes = face_ele_pt->nnode();
11728 // Get the first and last node
11729 Node* first_node_pt = face_ele_pt->node_pt(0);
11730 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
11731
11732 // Create the pair to store the nodes
11733 std::pair<Node*, Node*> edge =
11734 std::make_pair(first_node_pt, last_node_pt);
11735
11736 // Has the edge been included
11737 if (!edge_done[edge])
11738 {
11739 // Mark the edge as done
11740 edge_done[edge] = true;
11741
11742 // Create the reversed version and mark it as done too
11743 std::pair<Node*, Node*> inv_edge =
11744 std::make_pair(last_node_pt, first_node_pt);
11745
11746 // Mark the reversed edge as done
11747 edge_done[inv_edge] = true;
11748
11749 // Mark the edge to belong to boundary b
11750 element_edges_on_boundary[edge] = b;
11751 } // if (!edge_done[edge])
11752
11753 // Free the memory allocated for the face element
11754 delete face_ele_pt;
11755 face_ele_pt = 0;
11756
11757 } // for (e < nbound_ele)
11758
11759 } // for (b < nbound)
11760 }
11761
11762 // ======================================================================
11763 // Creates polylines from the intersection of halo elements on
11764 // all processors. The new polylines define the shared boundaries in
11765 // the domain This method computes the polylines on ALL processors,
11766 // that is why the three dimensions in the structure
11767 // output_polylines_pt[iproc][ncurve][npolyline]
11768 // ======================================================================
11769 template<class ELEMENT>
11771 const Vector<unsigned>& element_domain,
11772 std::map<GeneralisedElement*, unsigned>& element_to_global_index,
11773 std::set<FiniteElement*>& element_in_processor_pt,
11774 Vector<Vector<Vector<GeneralisedElement*>>>& input_halo_elements,
11775 std::map<std::pair<Node*, Node*>, unsigned>& elements_edges_on_boundary,
11776 Vector<Vector<Vector<TriangleMeshPolyLine*>>>& output_polylines_pt)
11777 {
11778 const unsigned nproc = this->communicator_pt()->nproc();
11779 const unsigned my_rank = this->communicator_pt()->my_rank();
11780
11781 // ---------------------------------------------------------------
11782 // Get the edges shared between each pair of processors
11783 // ---------------------------------------------------------------
11784
11785 // Storage for the edges (pair of nodes) shared between a pair of
11786 // processors
11788
11789 // Each edge is associated to two elements, a haloi (halo element
11790 // in processors i) and a haloj (halo element in processors j)
11791 Vector<Vector<Vector<Vector<FiniteElement*>>>> edge_element_pt(nproc);
11792
11793 // Each edge is associated to two elements, a haloi and a haloj,
11794 // the edge was created from a given face from each element, the
11795 // haloi face is stored at [0], the haloj face is stored at [1]
11796 Vector<Vector<Vector<Vector<int>>>> edge_element_face(nproc);
11797
11798 // Store the possible internal boundary id associated to each edge
11799 // (-1 if there is no association). Some edges may overlap an
11800 // internal boundary (and only internal boundaries)
11801 Vector<Vector<Vector<int>>> edge_boundary(nproc);
11802
11803 // Mark those edges (pair of nodes overlapped by a shared boundary)
11804 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
11805
11806 // Resize the containers, they store info. for each pair of
11807 // processors
11808
11809 // First resize the global container
11810 Shared_boundaries_ids.resize(nproc);
11811 for (unsigned j = 0; j < nproc; j++)
11812 {
11813 edges[j].resize(nproc);
11814 edge_element_pt[j].resize(nproc);
11815 edge_element_face[j].resize(nproc);
11816 edge_boundary[j].resize(nproc);
11817
11818 // Resize the global container for shared boundaries ids
11819 Shared_boundaries_ids[j].resize(nproc);
11820
11821 } // for (j < nproc)
11822
11823 // Take the halo elements of processor "iproc" and compare their
11824 // edges with halo elements of other processors (except itself)
11825 for (unsigned iproc = 0; iproc < nproc; iproc++)
11826 {
11827 // Take the halo elements of processor iproc and compare with
11828 // other processors
11829 // Start from the iproc + 1,
11830 // 1) To avoid comparing with itself,
11831 // 2) To avoid generation of repeated boundaries
11832 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
11833 {
11834 // **************************************************************
11835 // FIRST PART
11836 // 1) Get the halo elements of processor "iproc" with processor
11837 // "jproc"
11838 // 2) Get the halo elements of processor "jproc" with processor
11839 // "iproc"
11840 // 3) Compare their edges and those that match are the ones that
11841 // define the shared boundaries
11842 // **************************************************************
11843
11844 // Storage for halo elements
11845 Vector<GeneralisedElement*> halo_elements_iproc_with_jproc;
11846 Vector<GeneralisedElement*> halo_elements_jproc_with_iproc;
11847
11848 // Get the halo elements of "iproc" with "jproc"
11849 halo_elements_iproc_with_jproc = input_halo_elements[iproc][jproc];
11850
11851 // If there are halo elements then there are shared boundaries
11852 const unsigned nhalo_elements_iproc_with_jproc =
11853 halo_elements_iproc_with_jproc.size();
11854 // DEBP(nhalo_elements_iproc_with_jproc);
11855 if (nhalo_elements_iproc_with_jproc > 0)
11856 {
11857 // Get the halo elements of "jproc" with "iproc"
11858 halo_elements_jproc_with_iproc = input_halo_elements[jproc][iproc];
11859
11860 // If there are halo elements then there are shared
11861 // boundaries
11862 const unsigned nhalo_elements_jproc_with_iproc =
11863 halo_elements_jproc_with_iproc.size();
11864// DEBP(nhalo_elements_jproc_with_iproc);
11865#ifdef PARANOID
11866 if (nhalo_elements_jproc_with_iproc == 0)
11867 {
11868 // If there are halo elements of iproc with jproc there
11869 // MUST be halo elements on the other way round, not
11870 // necessary the same but at least one
11871 std::stringstream err;
11872 err << "There are no halo elements from processor (" << jproc
11873 << ") "
11874 << "with processor (" << iproc << ").\n"
11875 << "This is strange since there are halo elements from "
11876 << "processor (" << iproc << ") with processor (" << jproc
11877 << ").\n"
11878 << "Number of halo elements from (" << iproc << ") to ("
11879 << jproc << ") : (" << nhalo_elements_iproc_with_jproc << ")\n"
11880 << "Number of halo elements from (" << jproc << ") to ("
11881 << iproc << ") : (" << nhalo_elements_jproc_with_iproc << ")\n";
11882 throw OomphLibError(
11883 err.str(),
11884 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11885 OOMPH_EXCEPTION_LOCATION);
11886 }
11887#endif
11888 // The edges are defined as pair of nodes
11889 Vector<Node*> halo_edges_iproc;
11890 unsigned halo_edges_counter_iproc = 0;
11891 Vector<Node*> halo_edges_jproc;
11892 unsigned halo_edges_counter_jproc = 0;
11893
11894 // Map to associate the edge with the element used to create it
11895 std::map<std::pair<Node*, Node*>, FiniteElement*>
11896 edgesi_to_element_pt;
11897
11898 // Map to associated the edge with the face number of the
11899 // element that created it
11900 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11901 edgesi_element_pt_to_face_index;
11902
11903 // Map to associate the edge with the element used to create it
11904 std::map<std::pair<Node*, Node*>, FiniteElement*>
11905 edgesj_to_element_pt;
11906
11907 // Map to associated the edge with the face number of the
11908 // element that created it
11909 std::map<std::pair<std::pair<Node*, Node*>, FiniteElement*>, int>
11910 edgesj_element_pt_to_face_index;
11911
11912 // **************************************************************
11913 // 1.1) Store the edges of the "iproc" halo elements
11914 // **************************************************************
11915 // Go throught halo elements on "iproc" processor
11916 for (unsigned ih = 0; ih < nhalo_elements_iproc_with_jproc; ih++)
11917 {
11918#ifdef PARANOID
11919 unsigned e =
11920 element_to_global_index[halo_elements_iproc_with_jproc[ih]];
11921 // Only work with halo elements inside the "jproc" processor
11922 if (element_domain[e] != jproc)
11923 {
11924 // There was a problem on the ihalo-jhalo classification
11925 std::stringstream err;
11926 err << "There was a problem on the ihalo-jhalo classification.\n"
11927 << "One of the elements, (the one with the (" << e << ")-th "
11928 << "index ) is not on the (" << jproc << ")-th processor\n"
11929 << "but it was stored as a halo element of processor ("
11930 << iproc << ") with processor (" << jproc << ").\n";
11931 throw OomphLibError(
11932 err.str(),
11933 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11934 OOMPH_EXCEPTION_LOCATION);
11935 }
11936#endif
11937
11938 FiniteElement* el_pt =
11939 dynamic_cast<FiniteElement*>(halo_elements_iproc_with_jproc[ih]);
11940
11941 if (el_pt == 0)
11942 {
11943 std::stringstream err;
11944 err << "The halo element (" << ih
11945 << ") could not be casted to the "
11946 << "FiniteElement type.\n";
11947 throw OomphLibError(
11948 err.str(),
11949 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11950 OOMPH_EXCEPTION_LOCATION);
11951 }
11952
11953#ifdef PARANOID
11954 // Number of nodes on this element
11955 const unsigned n_nodes = el_pt->nnode();
11956
11957 // The number of nodes on every element should be at least
11958 // three since we are going to work with the cornes nodes,
11959 // the ones with index 0, 1 and 2
11960 if (n_nodes < 3)
11961 {
11962 std::stringstream err;
11963 err << "The number of nodes of the " << ih
11964 << "-th halo element is"
11965 << " (" << n_nodes << ").\nWe can not work with triangle "
11966 << "elements with less than three nodes\n";
11967 throw OomphLibError(
11968 err.str(),
11969 "TriangleMesh::create_polylines_from_halo_elements_helper()",
11970 OOMPH_EXCEPTION_LOCATION);
11971 }
11972#endif
11973
11974 // Get the corner nodes, the first three nodes
11975 Node* first_node_pt = el_pt->node_pt(0);
11976 Node* second_node_pt = el_pt->node_pt(1);
11977 Node* third_node_pt = el_pt->node_pt(2);
11978
11979 // Store the edges
11980 halo_edges_iproc.push_back(first_node_pt);
11981 halo_edges_iproc.push_back(second_node_pt);
11982 halo_edges_counter_jproc++;
11983
11984 halo_edges_iproc.push_back(second_node_pt);
11985 halo_edges_iproc.push_back(third_node_pt);
11986 halo_edges_counter_jproc++;
11987
11988 halo_edges_iproc.push_back(third_node_pt);
11989 halo_edges_iproc.push_back(first_node_pt);
11990 halo_edges_counter_jproc++;
11991
11992 // Store the info. of the element used to create these edges
11993 std::pair<Node*, Node*> edge1 =
11994 std::make_pair(first_node_pt, second_node_pt);
11995 edgesi_to_element_pt[edge1] = el_pt;
11996
11997 std::pair<Node*, Node*> edge2 =
11998 std::make_pair(second_node_pt, third_node_pt);
11999 edgesi_to_element_pt[edge2] = el_pt;
12000
12001 std::pair<Node*, Node*> edge3 =
12002 std::make_pair(third_node_pt, first_node_pt);
12003 edgesi_to_element_pt[edge3] = el_pt;
12004
12005 // Store the face index of the edge in the element
12006 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12007 std::make_pair(edge1, el_pt);
12008 edgesi_element_pt_to_face_index[edge_ele1] = 2;
12009
12010 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12011 std::make_pair(edge2, el_pt);
12012 edgesi_element_pt_to_face_index[edge_ele2] = 0;
12013
12014 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12015 std::make_pair(edge3, el_pt);
12016 edgesi_element_pt_to_face_index[edge_ele3] = 1;
12017
12018 } // for (ih < nhalo_elements_iproc_with_jproc)
12019
12020 // **************************************************************
12021 // 1.2) Store the edges of the "jproc" halo elements
12022 // **************************************************************
12023 // Go throught halo elements on "jproc" processor
12024 for (unsigned jh = 0; jh < nhalo_elements_jproc_with_iproc; jh++)
12025 {
12026#ifdef PARANOID
12027 unsigned e =
12028 element_to_global_index[halo_elements_jproc_with_iproc[jh]];
12029 // Only work with halo elements inside the "jproc" processor
12030 if (element_domain[e] != iproc)
12031 {
12032 // There was a problem on the jhalo-ihalo classification
12033 std::stringstream err;
12034 err << "There was a problem on the jhalo-ihalo classification.\n"
12035 << "One of the elements, (the one with the (" << e << ")-th "
12036 << "index ) is not on the (" << iproc << ")-th processor\n"
12037 << "but it was stored as a halo element of processor ("
12038 << jproc << ") with processor (" << iproc << ").\n";
12039 throw OomphLibError(
12040 err.str(),
12041 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12042 OOMPH_EXCEPTION_LOCATION);
12043 }
12044#endif
12045
12046 FiniteElement* el_pt =
12047 dynamic_cast<FiniteElement*>(halo_elements_jproc_with_iproc[jh]);
12048 if (el_pt == 0)
12049 {
12050 std::stringstream err;
12051 err << "The halo element (" << jh
12052 << ") could not be casted to the "
12053 << "FiniteElement type.\n";
12054 throw OomphLibError(
12055 err.str(),
12056 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12057 OOMPH_EXCEPTION_LOCATION);
12058 }
12059
12060#ifdef PARANOID
12061 // Number of nodes on this element
12062 const unsigned n_nodes = el_pt->nnode();
12063
12064 // The number of nodes on every element should be at least
12065 // three since we are going to work with the cornes nodes,
12066 // the ones with index 0, 1 and 2
12067 if (n_nodes < 3)
12068 {
12069 std::stringstream err;
12070 err << "The number of nodes of the " << jh
12071 << "-th halo element is"
12072 << " (" << n_nodes << ").\nWe can not work with triangle "
12073 << "elements with less than three nodes\n";
12074 throw OomphLibError(
12075 err.str(),
12076 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12077 OOMPH_EXCEPTION_LOCATION);
12078 }
12079#endif
12080
12081 // Get the nodes pointers
12082 Node* first_node_pt = el_pt->node_pt(0);
12083 Node* second_node_pt = el_pt->node_pt(1);
12084 Node* third_node_pt = el_pt->node_pt(2);
12085
12086 // Store the edges
12087 halo_edges_jproc.push_back(first_node_pt);
12088 halo_edges_jproc.push_back(second_node_pt);
12089 halo_edges_counter_iproc++;
12090
12091 halo_edges_jproc.push_back(second_node_pt);
12092 halo_edges_jproc.push_back(third_node_pt);
12093 halo_edges_counter_iproc++;
12094
12095 halo_edges_jproc.push_back(third_node_pt);
12096 halo_edges_jproc.push_back(first_node_pt);
12097 halo_edges_counter_iproc++;
12098
12099 // Store the info. of the element used to create these edges
12100 std::pair<Node*, Node*> edge1 =
12101 std::make_pair(first_node_pt, second_node_pt);
12102 edgesj_to_element_pt[edge1] = el_pt;
12103
12104 std::pair<Node*, Node*> edge2 =
12105 std::make_pair(second_node_pt, third_node_pt);
12106 edgesj_to_element_pt[edge2] = el_pt;
12107
12108 std::pair<Node*, Node*> edge3 =
12109 std::make_pair(third_node_pt, first_node_pt);
12110 edgesj_to_element_pt[edge3] = el_pt;
12111
12112 // Store the face index of the edge in the element
12113 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele1 =
12114 std::make_pair(edge1, el_pt);
12115 edgesj_element_pt_to_face_index[edge_ele1] = 2;
12116
12117 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele2 =
12118 std::make_pair(edge2, el_pt);
12119 edgesj_element_pt_to_face_index[edge_ele2] = 0;
12120
12121 std::pair<std::pair<Node*, Node*>, FiniteElement*> edge_ele3 =
12122 std::make_pair(edge3, el_pt);
12123 edgesj_element_pt_to_face_index[edge_ele3] = 1;
12124
12125 } // for (jh < nhalo_elements_jproc_with_iproc)
12126
12127 // ***************************************************************
12128 // SECOND PART
12129 // 1) We already have the information of the edges on the iproc
12130 // halo and jproc halo elements
12131 // 2) Identify the shared edges to create the shared boundaries
12132 // (Only store the information but do not create the polyline)
12133 // ***************************************************************
12134
12135 // Get the number of edges from each processor
12136 unsigned nhalo_iedges = halo_edges_iproc.size();
12137 unsigned nhalo_jedges = halo_edges_jproc.size();
12138
12139 // Start comparing the edges to check which of those are
12140 // shared between the "ihalo_edge" and the "jhalo_edge"
12141 for (unsigned ihe = 0; ihe < nhalo_iedges; ihe += 2)
12142 {
12143 // Get the ihe-th edge (pair of nodes)
12144 Vector<Node*> ihalo_edge(2);
12145 ihalo_edge[0] = halo_edges_iproc[ihe];
12146 ihalo_edge[1] = halo_edges_iproc[ihe + 1];
12147
12148 // Create the pair that defines the edge
12149 std::pair<Node*, Node*> tmp_edge =
12150 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12151
12152 // Check if the edge lies on a boundary (default values is
12153 // -1 for no association with an internal boundary)
12154 int edge_boundary_id = -1;
12155 {
12156 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
12157 it = elements_edges_on_boundary.find(tmp_edge);
12158 // If the edges lie on a boundary then get the boundary id
12159 // on which the edges lie
12160 if (it != elements_edges_on_boundary.end())
12161 {
12162 // Assign the internal boundary id associated with the
12163 // edge
12164 edge_boundary_id = (*it).second;
12165 }
12166 else
12167 {
12168 // Look for the reversed version of the edge (the nodes
12169 // inverted)
12170 std::pair<Node*, Node*> rtmp_edge =
12171 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12172 it = elements_edges_on_boundary.find(rtmp_edge);
12173 if (it != elements_edges_on_boundary.end())
12174 {
12175 // Assign the internal boundary id associated with the
12176 // edge
12177 edge_boundary_id = (*it).second;
12178 }
12179 }
12180 }
12181
12182 // Go through the jhalo_edge and compare with the
12183 // ihalo_edge
12184 for (unsigned jhe = 0; jhe < nhalo_jedges; jhe += 2)
12185 {
12186 // Get the jhe-th edge (pair of nodes)
12187 Vector<Node*> jhalo_edge(2);
12188 jhalo_edge[0] = halo_edges_jproc[jhe];
12189 jhalo_edge[1] = halo_edges_jproc[jhe + 1];
12190
12191 // Comparing pointer of nodes
12192 if (ihalo_edge[0] == jhalo_edge[0] &&
12193 ihalo_edge[1] == jhalo_edge[1])
12194 {
12195 // Create the edge (both nodes that make the edge)
12196 std::pair<Node*, Node*> new_edge =
12197 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12198
12199 // Get the elements involved in the creation of the
12200 // edge to check that there are elements associated to
12201 // the edge
12202 FiniteElement* haloi_ele_pt = 0;
12203 haloi_ele_pt = edgesi_to_element_pt[new_edge];
12204 FiniteElement* haloj_ele_pt = 0;
12205 haloj_ele_pt = edgesj_to_element_pt[new_edge];
12206
12207 // Verify that there is an element associated with it
12208 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12209 {
12210 std::stringstream err;
12211 err << "There is no associated elements with the new "
12212 << "shared boundary. This is an storing problem,\n"
12213 << "possibly related with a memory leak problem!!!\n"
12214 << "The nodes that compound the edge are these:\n"
12215 << "On processor (" << iproc << "):\n"
12216 << "(" << ihalo_edge[0]->x(0) << ", "
12217 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12218 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12219 << "On processor (" << jproc << "):\n"
12220 << "(" << jhalo_edge[0]->x(0) << ", "
12221 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12222 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12223 << "The nodes coordinates should be the same!!!\n";
12224 throw OomphLibError(err.str(),
12225 "TriangleMesh::create_polylines_from_"
12226 "halo_elements_helper()",
12227 OOMPH_EXCEPTION_LOCATION);
12228 }
12229
12230 // Store the edge
12231 edges[iproc][jproc].push_back(new_edge);
12232
12233 // Is the edge overlapped by a shared boundary
12234 if (edge_boundary_id >= 0)
12235 {
12236 // Mark the edge as overlapped
12237 overlapped_edge[new_edge] = true;
12238
12239 // Also mark the reversed edge
12240 std::pair<Node*, Node*> rev_new_edge =
12241 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12242
12243 // Mark the edge as overlapped
12244 overlapped_edge[rev_new_edge] = true;
12245
12246 } // if (edge_boundary_id >= 0)
12247
12248 // Store the internal boundary id (default -1)
12249 // associated to the edge
12250 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12251
12252 // Store the two elements associated with the edge
12253 Vector<FiniteElement*> tmp_elements_pt;
12254 tmp_elements_pt.push_back(haloi_ele_pt);
12255 tmp_elements_pt.push_back(haloj_ele_pt);
12256
12257 // Associate the edge with the elements that gave rise to it
12258 edge_element_pt[iproc][jproc].push_back(tmp_elements_pt);
12259
12260 // Get the face index on each element that gave rise to
12261 // the edge
12262
12263 // .. first create the pair (edge, finite_element)
12264 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12265 edge_elementi_pair = make_pair(new_edge, haloi_ele_pt);
12266
12267 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12268 edge_elementj_pair = make_pair(new_edge, haloj_ele_pt);
12269
12270 // Set default values to later check if values were
12271 // read from the map structure
12272 int face_index_haloi_ele = -1;
12273 face_index_haloi_ele =
12274 edgesi_element_pt_to_face_index[edge_elementi_pair];
12275 int face_index_haloj_ele = -1;
12276 face_index_haloj_ele =
12277 edgesj_element_pt_to_face_index[edge_elementj_pair];
12278 // Verify that there is an element associated with it
12279 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12280 {
12281 std::stringstream err;
12282 err << "There is no associated face indexes to the"
12283 << "elements that gave\nrise to the shared edge\n"
12284 << "The nodes that compound the edge are these:\n"
12285 << "On processor (" << iproc << "):\n"
12286 << "(" << ihalo_edge[0]->x(0) << ", "
12287 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12288 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12289 << "On processor (" << jproc << "):\n"
12290 << "(" << jhalo_edge[0]->x(0) << ", "
12291 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12292 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12293 << "The nodes coordinates should be the same!!!\n";
12294 throw OomphLibError(err.str(),
12295 "TriangleMesh::create_polylines_from_"
12296 "halo_elements_helper()",
12297 OOMPH_EXCEPTION_LOCATION);
12298 } // if (face_index_haloi_ele == -1 ||
12299 // face_index_haloj_ele == -1)
12300
12301 // Get the face indexes from the map structure
12302 Vector<int> tmp_edge_element_face_index;
12303 tmp_edge_element_face_index.push_back(face_index_haloi_ele);
12304 tmp_edge_element_face_index.push_back(face_index_haloj_ele);
12305 // Store the face indexes
12306 edge_element_face[iproc][jproc].push_back(
12307 tmp_edge_element_face_index);
12308
12309 break; // break for (jhe < nhalo_jedges) since edge
12310 // found
12311
12312 } // if (ihalo_edge[0] == jhalo_edge[0] &&
12313 // ihalo_edge[1] == jhalo_edge[1])
12314 // Comparing nodes pointers
12315 else if (ihalo_edge[0] == jhalo_edge[1] &&
12316 ihalo_edge[1] == jhalo_edge[0])
12317 {
12318 // Create the edge (both nodes that make the edge)
12319 std::pair<Node*, Node*> new_edge =
12320 std::make_pair(ihalo_edge[0], ihalo_edge[1]);
12321
12322 // Get the elements involved in the creation of the
12323 // edge
12324 FiniteElement* haloi_ele_pt = 0;
12325 haloi_ele_pt = edgesi_to_element_pt[new_edge];
12326
12327 FiniteElement* haloj_ele_pt = 0;
12328 // Create the edge (reversed, that is how it was
12329 // originally stored)
12330 std::pair<Node*, Node*> new_edge_reversed =
12331 std::make_pair(jhalo_edge[0], jhalo_edge[1]);
12332 haloj_ele_pt = edgesj_to_element_pt[new_edge_reversed];
12333
12334 // Verify that there is an element associated with it
12335 if (haloi_ele_pt == 0 || haloj_ele_pt == 0)
12336 {
12337 std::stringstream err;
12338 err << "There is no associated elements with the new "
12339 << "shared boundary (reversed version). This is an "
12340 << "storing problem, possibly related with a memory "
12341 << "leak problem!!!\n"
12342 << "The nodes that compound the edge are these:\n"
12343 << "On processor (" << iproc << "):\n"
12344 << "(" << ihalo_edge[0]->x(0) << ", "
12345 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12346 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12347 << "On processor (" << jproc << "):\n"
12348 << "(" << jhalo_edge[0]->x(0) << ", "
12349 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12350 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12351 << "The nodes coordinates should be the same!!!\n";
12352 throw OomphLibError(err.str(),
12353 "TriangleMesh::create_polylines_from_"
12354 "halo_elements_helper()",
12355 OOMPH_EXCEPTION_LOCATION);
12356 }
12357
12358 // Store the edge
12359 edges[iproc][jproc].push_back(new_edge);
12360
12361 // Is the edge overlapped by a shared boundary
12362 if (edge_boundary_id >= 0)
12363 {
12364 // Mark the edge as overlapped
12365 overlapped_edge[new_edge] = true;
12366
12367 // Also mark the reversed edge
12368 std::pair<Node*, Node*> rev_new_edge =
12369 std::make_pair(ihalo_edge[1], ihalo_edge[0]);
12370
12371 // Mark the edge as overlapped
12372 overlapped_edge[rev_new_edge] = true;
12373 } // if (edge_boundary_id >= 0)
12374
12375 // Store the internal boundary id (default -1)
12376 // associated to the edge
12377 edge_boundary[iproc][jproc].push_back(edge_boundary_id);
12378
12379 // Store the two elements associated with the edge
12380 Vector<FiniteElement*> tmp_elements_pt;
12381 tmp_elements_pt.push_back(haloi_ele_pt);
12382 tmp_elements_pt.push_back(haloj_ele_pt);
12383
12384 // Associate the edge with the elements that gave rise to it
12385 edge_element_pt[iproc][jproc].push_back(tmp_elements_pt);
12386
12387 // Get the face index on each element that gave rise to
12388 // the edge
12389
12390 // .. first create the pair (edge, finite_element)
12391 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12392 edge_elementi_pair = make_pair(new_edge, haloi_ele_pt);
12393
12394 std::pair<std::pair<Node*, Node*>, FiniteElement*>
12395 edge_elementj_pair =
12396 make_pair(new_edge_reversed, haloj_ele_pt);
12397
12398 // Set default values to later check if values were
12399 // read from the map structure
12400 int face_index_haloi_ele = -1;
12401 face_index_haloi_ele =
12402 edgesi_element_pt_to_face_index[edge_elementi_pair];
12403 int face_index_haloj_ele = -1;
12404 face_index_haloj_ele =
12405 edgesj_element_pt_to_face_index[edge_elementj_pair];
12406 // Verify that there is an element associated with it
12407 if (face_index_haloi_ele == -1 || face_index_haloj_ele == -1)
12408 {
12409 std::stringstream err;
12410 err << "There is no associated face indexes to the"
12411 << "elements that gave\nrise to the shared edge\n"
12412 << "The nodes that compound the edge are these:\n"
12413 << "On processor (" << iproc << "):\n"
12414 << "(" << ihalo_edge[0]->x(0) << ", "
12415 << ihalo_edge[0]->x(1) << ") and (" << ihalo_edge[1]->x(0)
12416 << ", " << ihalo_edge[1]->x(1) << ")\n\n"
12417 << "On processor (" << jproc << "):\n"
12418 << "(" << jhalo_edge[0]->x(0) << ", "
12419 << jhalo_edge[0]->x(1) << ") and (" << jhalo_edge[1]->x(0)
12420 << ", " << jhalo_edge[1]->x(1) << ")\n\n"
12421 << "The nodes coordinates should be the same!!!\n";
12422 throw OomphLibError(err.str(),
12423 "TriangleMesh::create_polylines_from_"
12424 "halo_elements_helper()",
12425 OOMPH_EXCEPTION_LOCATION);
12426 } // if (face_index_haloi_ele == -1 ||
12427 // face_index_haloj_ele == -1)
12428
12429 // Get the face indexes from the map structure
12430 Vector<int> tmp_edge_element_face_index;
12431 tmp_edge_element_face_index.push_back(face_index_haloi_ele);
12432 tmp_edge_element_face_index.push_back(face_index_haloj_ele);
12433 // Store the face indexes
12434 edge_element_face[iproc][jproc].push_back(
12435 tmp_edge_element_face_index);
12436
12437 break; // break for (jhe < nhalo_jedges) since edge found
12438
12439 } // else if (ihalo_edge[0] == jhalo_edge[1] &&
12440 // ihalo_edge[1] == jhalo_edge[0])
12441
12442 } // for (jhe < nhaloj_edges)
12443
12444 } // for (ihe < nhaloi_edges)
12445
12446 } // if (nhalo_elements_iproc_with_jproc > 0)
12447
12448 } // for (jproc < nproc)
12449
12450 } // for (iproc < nproc)
12451
12452 // ------------------------------------------------------------------
12453 // Compute the degree of each node in the shared edges
12454 // ------------------------------------------------------------------
12455
12456 // Visit all the shared edges between each pair of processors,
12457 // visit the nodes of each edge and compute the degree of each node
12458
12459 // Store the degree (valency) of each node
12460 std::map<Node*, unsigned> global_shared_node_degree;
12461
12462#ifdef PARANOID
12463 // Map to check if an edge has been already visited
12464 std::map<std::pair<Node*, Node*>, bool> edge_done;
12465#endif // #ifdef PARANOID
12466 // Map to check if a node has been already visited
12467 std::map<Node*, bool> node_done;
12468
12469 // Loop over the processors and get the shared edged between each
12470 // pair of processors
12471 for (unsigned iproc = 0; iproc < nproc; iproc++)
12472 {
12473 // Start from iproc + 1 to avoid checking with itself (there is
12474 // no shared edges between the same processor), and to avoid
12475 // double counting the edges and nodes (the shared edges between
12476 // processor (iproc, jproc) are the same as those between
12477 // processor jproc, iproc)
12478 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12479 {
12480 // Get the number of edges shared between the pair of processors
12481 const unsigned nshd_edges = edges[iproc][jproc].size();
12482#ifdef PARANOID
12483 // There must be the same number of information on each of the
12484 // containers
12485
12486 // Get the number of edge elements
12487 const unsigned nedge_element = edge_element_pt[iproc][jproc].size();
12488 if (nshd_edges != nedge_element)
12489 {
12490 std::stringstream error_message;
12491 error_message
12492 << "The number of shared edges between processor iproc and jproc\n"
12493 << "is different form the number of edge elements between the\n"
12494 << "pair of processors\n"
12495 << "iproc: (" << iproc << ")\n"
12496 << "jproc: (" << jproc << ")\n"
12497 << "# of shared edges: (" << nshd_edges << ")\n"
12498 << "# of edge elements: (" << nedge_element << ")\n\n";
12499 throw OomphLibError(
12500 error_message.str(),
12501 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12502 OOMPH_EXCEPTION_LOCATION);
12503 }
12504
12505 // Get the number of edge element faces
12506 const unsigned nedge_element_face =
12507 edge_element_face[iproc][jproc].size();
12508 if (nshd_edges != nedge_element_face)
12509 {
12510 std::stringstream error_message;
12511 error_message
12512 << "The number of shared edges between processor iproc and jproc\n"
12513 << "is different form the number of edge element faces between "
12514 "the\n"
12515 << "pair of processors\n"
12516 << "iproc: (" << iproc << ")\n"
12517 << "jproc: (" << jproc << ")\n"
12518 << "# of shared edges: (" << nshd_edges << ")\n"
12519 << "# of edge element faces: (" << nedge_element_face << ")\n\n";
12520 throw OomphLibError(
12521 error_message.str(),
12522 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12523 OOMPH_EXCEPTION_LOCATION);
12524 }
12525
12526 // Get the number of edge boundaries
12527 const unsigned nedge_boundary = edge_boundary[iproc][jproc].size();
12528 if (nshd_edges != nedge_boundary)
12529 {
12530 std::stringstream error_message;
12531 error_message
12532 << "The number of shared edges between processor iproc and jproc\n"
12533 << "is different form the number of edge boundaries ids between "
12534 "the\n"
12535 << "pair of processors\n"
12536 << "iproc: (" << iproc << ")\n"
12537 << "jproc: (" << jproc << ")\n"
12538 << "# of shared edges: (" << nshd_edges << ")\n"
12539 << "# of edge boundaries ids: (" << nedge_boundary << ")\n\n";
12540 throw OomphLibError(
12541 error_message.str(),
12542 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12543 OOMPH_EXCEPTION_LOCATION);
12544 }
12545
12546#endif // #ifdef PARANOID
12547
12548 // Loop over the shared edges between (iproc, jproc) processors
12549 for (unsigned se = 0; se < nshd_edges; se++)
12550 {
12551 // Get the edge
12552 std::pair<Node*, Node*> edge = edges[iproc][jproc][se];
12553#ifdef PARANOID
12554 // Check that the edge has not been previously visited
12555 if (edge_done[edge])
12556 {
12557 std::stringstream error_message;
12558 error_message
12559 << "The shared edge between processor iproc and processor\n"
12560 << "jproc has been already visited, this is weird since the\n"
12561 << "edge should not be shared by other pair of processors\n"
12562 << "iproc: (" << iproc << ")\n"
12563 << "jproc: (" << jproc << ")\n"
12564 << "First node of edge: (" << edge.first->x(0) << ", "
12565 << edge.first->x(1) << ")\n"
12566 << "Second node of edge: (" << edge.second->x(0) << ", "
12567 << edge.second->x(1) << ")\n"
12568 << "Associated edge boundary id: ("
12569 << edge_boundary[iproc][jproc][se] << ")\n\n";
12570 throw OomphLibError(
12571 error_message.str(),
12572 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12573 OOMPH_EXCEPTION_LOCATION);
12574 }
12575
12576 // Mark the edge as done
12577 edge_done[edge] = true;
12578 // Create the reversed version and include it too
12579 std::pair<Node*, Node*> rev_edge =
12580 std::make_pair(edge.second, edge.first);
12581 // Mark reversed edge as done
12582 edge_done[rev_edge] = true;
12583#endif // #ifdef PARANOID
12584
12585 // Get each of the nodes that conform the edge
12586 Node* left_node_pt = edge.first;
12587 Node* right_node_pt = edge.second;
12588
12589 // Check if the left node has been already done
12590 if (!node_done[left_node_pt])
12591 {
12592 // Set the degree of the node to once since this is the
12593 // first time it has been found
12594 global_shared_node_degree[left_node_pt] = 1;
12595
12596 } // if (!done_node[left_node_pt])
12597 else
12598 {
12599 // Increase the degree of the node
12600 global_shared_node_degree[left_node_pt]++;
12601 }
12602
12603 // Check if the right node has been already done
12604 if (!node_done[right_node_pt])
12605 {
12606 // Set the degree of the node to once since this is the
12607 // first time it has been found
12608 global_shared_node_degree[right_node_pt] = 1;
12609 } // if (!done_node[right_node_pt])
12610 else
12611 {
12612 // Increase the degree of the node
12613 global_shared_node_degree[right_node_pt]++;
12614 }
12615
12616 } // for (se < nshd_edges)
12617
12618 } // for (jproc < nproc)
12619
12620 } // for (iproc < nproc)
12621
12622 // -----------------------------------------------------------------
12623 // Identify those nodes living on edges of original boundaries not
12624 // overlapped by a shared boundary
12625
12626 // Mark the nodes on original boundaries not overlapped by shared
12627 // boundaries
12628 std::map<unsigned, std::map<Node*, bool>>
12629 node_on_bnd_not_overlapped_by_shd_bnd;
12630
12631 // Loop over the edges of the original boundaries
12632 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
12633 elements_edges_on_boundary.begin();
12634 it_map != elements_edges_on_boundary.end();
12635 it_map++)
12636 {
12637 // Get the edge
12638 std::pair<Node*, Node*> edge_pair = (*it_map).first;
12639
12640 // Is the edge overlaped by a shared boundary
12641 if (!overlapped_edge[edge_pair])
12642 {
12643 // Mark the nodes of the edge as being on an edge not overlaped
12644 // by a shared boundary on the boundary the edge is
12645 unsigned b = (*it_map).second;
12646
12647 // Get the left node
12648 Node* left_node_pt = edge_pair.first;
12649 node_on_bnd_not_overlapped_by_shd_bnd[b][left_node_pt] = true;
12650
12651 // Get the right node
12652 Node* right_node_pt = edge_pair.second;
12653 node_on_bnd_not_overlapped_by_shd_bnd[b][right_node_pt] = true;
12654
12655 } // if (!overlapped_edge[edge_pair])
12656
12657 } // Loop over edges to mark those nodes on overlaped edge by
12658 // shared boundaries
12659
12660 // ------------------------------------------------------------------
12661 // Now create the shared polylines but including the degree of the
12662 // nodes as a nw stop condition for adding more edges to the side
12663 // or a root edge
12664 // ------------------------------------------------------------------
12665
12666 // Storage for new created polylines with "each processor", non
12667 // sorted (shared polylines of the current processor only)
12668 Vector<Vector<TriangleMeshPolyLine*>> unsorted_polylines_pt(nproc);
12669
12670 // Map that associates the shared boundary id with the list of
12671 // nodes that create it (shared boundary of the current processor
12672 // only)
12673 std::map<unsigned, std::list<Node*>> shared_bnd_id_to_sorted_list_node_pt;
12674
12675 // Get maximum user boundary id and set the initial shared boundary
12676 // id
12677 unsigned shared_boundary_id_start = this->nboundary();
12678 Initial_shared_boundary_id = shared_boundary_id_start;
12679
12680 // Aqui
12681
12682 // Loop over the processors and get the shared edged between each
12683 // pair of processors
12684 for (unsigned iproc = 0; iproc < nproc; iproc++)
12685 {
12686 // Start from iproc + 1 to avoid checking with itself (there is
12687 // no shared edges between the same processor), and to avoid
12688 // double counting the edges and nodes (the shared edges between
12689 // processor (iproc, jproc) are the same as those between
12690 // processor jproc, iproc)
12691 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
12692 {
12693 // *************************************************************
12694 // THIRD PART
12695 // 1) Sort the edges (make them contiguous) so that they can
12696 // be used as the vertex coordinates that define a shared
12697 // boundary (polyline)
12698 // *************************************************************
12699 unsigned npolylines_counter = 0;
12700 const unsigned nedges = edges[iproc][jproc].size();
12701
12702 // -----------------------------------------------------------
12703 // Compute all the SHARED POLYLINES
12704 // -----------------------------------------------------------
12705 // The number of sorted edges
12706 unsigned nsorted_edges = 0;
12707
12708 // Keep track of the already done edges
12709 std::map<std::pair<Node*, Node*>, bool> edge_done;
12710
12711 // Loop over all the edges to create all the polylines with
12712 // the current processors involved
12713 while (nsorted_edges < nedges)
12714 {
12715 // Temporaly storage for the elements associated to the
12716 // sorted edges
12717 std::list<FiniteElement*> tmp_boundary_element_pt;
12718 // Temporly storage for the face indexes on the element
12719 // that created the given edge
12720 std::list<int> tmp_face_index_element;
12721 // Get an initial pair of nodes to create an edge
12722 std::pair<Node*, Node*> edge;
12723#ifdef PARANOID
12724 bool found_initial_edge = false;
12725#endif
12726 int root_edge_bound_id = -1;
12727 unsigned iedge = 0;
12728 for (iedge = 0; iedge < nedges; iedge++)
12729 {
12730 edge = edges[iproc][jproc][iedge];
12731 // If not done then take it as initial edge
12732 if (!edge_done[edge])
12733 {
12734 // Get the boundary id that the edge may be overlapping
12735 root_edge_bound_id = edge_boundary[iproc][jproc][iedge];
12736#ifdef PARANOID
12737 found_initial_edge = true;
12738#endif
12739 nsorted_edges++;
12740 iedge++;
12741 break;
12742 } // if (!edge_done[edge])
12743 } // for (iedge < nedges)
12744
12745#ifdef PARANOID
12746 if (!found_initial_edge)
12747 {
12748 std::ostringstream error_message;
12749 error_message
12750 << "All the edge are already done, but the number of done\n"
12751 << "edges (" << nsorted_edges
12752 << ") is still less than the total\n"
12753 << "number of edges (" << nedges << ").\n";
12754 // << "----- Possible memory leak -----\n";
12755 throw OomphLibError(
12756 error_message.str(),
12757 "TriangleMesh::create_polylines_from_halo_elements_helper()",
12758 OOMPH_EXCEPTION_LOCATION);
12759 }
12760#endif
12761
12762 // Storing for the sorting nodes extracted from the
12763 // edges. The sorted nodes are used to create a polyline
12764 std::list<Node*> sorted_nodes;
12765 sorted_nodes.clear();
12766
12767 // The initial and final nodes of the list
12768 Node* first_node_pt = edge.first;
12769 Node* last_node_pt = edge.second;
12770
12771 // Push back on the list the new edge (nodes)
12772 sorted_nodes.push_back(first_node_pt);
12773 sorted_nodes.push_back(last_node_pt);
12774
12775 // Get the elements associated to the edge and store them
12776 // in the temporaly boundary elements storage
12777 tmp_boundary_element_pt.push_back(
12778 edge_element_pt[iproc][jproc][iedge - 1][0]);
12779 tmp_boundary_element_pt.push_back(
12780 edge_element_pt[iproc][jproc][iedge - 1][1]);
12781
12782 // ... then get the face index of the element from where
12783 // the edge came from
12784 tmp_face_index_element.push_back(
12785 edge_element_face[iproc][jproc][iedge - 1][0]);
12786 tmp_face_index_element.push_back(
12787 edge_element_face[iproc][jproc][iedge - 1][1]);
12788
12789 // Mark edge as done
12790 edge_done[edge] = true;
12791
12792 // Continue iterating if a new node (that creates a new
12793 // edge) is added to the list, we have just added two nodes
12794 // (the first and last of the root edge)
12795 bool node_added = true;
12796
12797 // Flags to indicate at which end the node was added (left
12798 // or right)
12799 bool node_added_to_the_left = true;
12800 bool node_added_to_the_right = true;
12801
12802 // The nodes that create a shared boundary are obtained by
12803 // connecting the edges shared by the halo and haloed
12804 // elements. These edges are connected to left or right of
12805 // the shared boundary. Every time a new edge is added to
12806 // the left (or right), the most left (or right) node is
12807 // searched in the list of nodes of previous shared
12808 // boundaries, if the node is found then it is said to be
12809 // shared with another boundary and a connection to that
12810 // boundary needs to be specified. We stop adding edges
12811 // (and nodes) to the side where that nodes was found to be
12812 // shared. Note that the intersection (shared node) may be
12813 // with the same shared boundary
12814
12815 // Flag to indicate a node was found to be shared with
12816 // another boundary at the left end (most left node) of the
12817 // shared boundary
12818 bool connection_to_the_left = false;
12819
12820 // Flag to indicate a node was found to be shared with
12821 // another boundary at the right end (most right node) of
12822 // the shared boundary
12823 bool connection_to_the_right = false;
12824
12825 // Flag to stop the adding of edges (and nodes) to the
12826 // current shared boundary
12827 bool current_polyline_has_connections_at_both_ends = false;
12828
12829 // Store the boundary ids of the polylines to connect (only
12830 // used when the polyline was found to have a connection)
12831 // -1: Indicates no connection
12832 // -2: Indicates connection with itself
12833 // Any other value: Boundary id to connect
12834 int bound_id_connection_to_the_left = -1;
12835 int bound_id_connection_to_the_right = -1;
12836
12837 // Get the degree of the first node
12838 const unsigned first_node_degree =
12839 global_shared_node_degree[first_node_pt];
12840
12841 // Check if the nodes of the root edge have connections
12842 // ... to the left
12843 bound_id_connection_to_the_left = check_connections_of_polyline_nodes(
12844 element_in_processor_pt,
12845 root_edge_bound_id,
12846 overlapped_edge,
12847 node_on_bnd_not_overlapped_by_shd_bnd,
12848 sorted_nodes,
12849 shared_bnd_id_to_sorted_list_node_pt,
12850 first_node_degree,
12851 first_node_pt);
12852
12853 // If there is a connection then set the
12854 // corresponding flag
12855 // (-1): No connection
12856 // (-2): Connection with itself
12857 // (-3): No connection, stop adding nodes
12858 // (other value): Boundary id
12859 if (bound_id_connection_to_the_left != -1)
12860 {
12861 connection_to_the_left = true;
12862 } // if (bound_id_connection_to_the_left != -1)
12863
12864 // Get the degree of the last node
12865 const unsigned last_node_degree =
12866 global_shared_node_degree[last_node_pt];
12867
12868 // Check if the nodes of the root edge have connections
12869 // ... to the right
12870 bound_id_connection_to_the_right =
12871 check_connections_of_polyline_nodes(
12872 element_in_processor_pt,
12873 root_edge_bound_id,
12874 overlapped_edge,
12875 node_on_bnd_not_overlapped_by_shd_bnd,
12876 sorted_nodes,
12877 shared_bnd_id_to_sorted_list_node_pt,
12878 last_node_degree,
12879 last_node_pt);
12880
12881 // If there is a connection then set the
12882 // corresponding flag
12883 // (-1): No connection
12884 // (-2): Connection with itself
12885 // (other value): Boundary id
12886 if (bound_id_connection_to_the_right != -1)
12887 {
12888 connection_to_the_right = true;
12889 } // if (bound_id_connection_to_the_right != -1)
12890
12891 // If the current shared boundary has connections at both
12892 // ends then stop the adding of nodes
12893 if (connection_to_the_left && connection_to_the_right)
12894 {
12895 current_polyline_has_connections_at_both_ends = true;
12896 }
12897
12898 // Continue searching for more edges if
12899 // 1) A new node was added at the left or right of the list
12900 // 2) There are more edges to possible add
12901 // 3) The added node is not part of any other previous
12902 // shared polyline
12903 while (node_added && (nsorted_edges < nedges) &&
12904 !current_polyline_has_connections_at_both_ends)
12905 {
12906 // Start from the next edge since we have already added
12907 // the previous one as the initial edge (any previous
12908 // edge had to be added to previous polylines)
12909 for (unsigned iiedge = iedge; iiedge < nedges; iiedge++)
12910 {
12911 // Reset the flags for added nodes, to the left and right
12912 node_added = false;
12913 node_added_to_the_left = false;
12914 node_added_to_the_right = false;
12915 // Get the current edge
12916 edge = edges[iproc][jproc][iiedge];
12917 const int edge_bound_id = edge_boundary[iproc][jproc][iiedge];
12918
12919 // We need to ensure to connect with edges that share
12920 // the same bound id or with those that has no boundary
12921 // id associated (the default -1 value), may apply
12922 // exclusively to internal boundaries
12923 if (!edge_done[edge] && (edge_bound_id == root_edge_bound_id))
12924 {
12925 // Get each individual node
12926 Node* left_node_pt = edge.first;
12927 Node* right_node_pt = edge.second;
12928
12929 // Pointer to the new added node
12930 Node* new_added_node_pt = 0;
12931
12932 // Is the node to be added to the left?
12933 if (left_node_pt == first_node_pt && !connection_to_the_left)
12934 {
12935 // Push front the new node
12936 sorted_nodes.push_front(right_node_pt);
12937 // Update the new added node and the first node
12938 new_added_node_pt = first_node_pt = right_node_pt;
12939 // Set the node added flag to true
12940 node_added = true;
12941 // Indicate the node was added to the left
12942 node_added_to_the_left = true;
12943 }
12944 // Is the node to be added to the right?
12945 else if (left_node_pt == last_node_pt &&
12946 !connection_to_the_right)
12947 {
12948 // Push back the new node
12949 sorted_nodes.push_back(right_node_pt);
12950 // Update the new added node and the last node
12951 new_added_node_pt = last_node_pt = right_node_pt;
12952 // Set the node added flag to true
12953 node_added = true;
12954 // Indicate the node was added to the right
12955 node_added_to_the_right = true;
12956 }
12957 // Is the node to be added to the left?
12958 else if (right_node_pt == first_node_pt &&
12959 !connection_to_the_left)
12960 {
12961 // Push front the new node
12962 sorted_nodes.push_front(left_node_pt);
12963 // Update the new added node and the first node
12964 new_added_node_pt = first_node_pt = left_node_pt;
12965 // Set the node added flag to true
12966 node_added = true;
12967 // Indicate the node was added to the left
12968 node_added_to_the_left = true;
12969 }
12970 // Is the node to be added to the right?
12971 else if (right_node_pt == last_node_pt &&
12972 !connection_to_the_right)
12973 {
12974 // Push back the new node
12975 sorted_nodes.push_back(left_node_pt);
12976 // Update the new added node and the last node
12977 new_added_node_pt = last_node_pt = left_node_pt;
12978 // Set the node added flag to true
12979 node_added = true;
12980 // Indicate the node was added to the right
12981 node_added_to_the_right = true;
12982 }
12983
12984 // If we added a new node then we need to check if
12985 // that node has been already added in other shared
12986 // boundaries (which may define a connection)
12987 if (node_added)
12988 {
12989 // Mark as done only if one of its nodes has been
12990 // added to the list
12991 edge_done[edge] = true;
12992 nsorted_edges++;
12993
12994 // Get the degree of the added node
12995 const unsigned added_node_degree =
12996 global_shared_node_degree[new_added_node_pt];
12997
12998 if (node_added_to_the_left)
12999 {
13000 // Add the bulk elements
13001 tmp_boundary_element_pt.push_front(
13002 edge_element_pt[iproc][jproc][iiedge][1]);
13003 tmp_boundary_element_pt.push_front(
13004 edge_element_pt[iproc][jproc][iiedge][0]);
13005 // Add the face elements
13006 tmp_face_index_element.push_front(
13007 edge_element_face[iproc][jproc][iiedge][1]);
13008 tmp_face_index_element.push_front(
13009 edge_element_face[iproc][jproc][iiedge][0]);
13010 }
13011
13012 if (node_added_to_the_right)
13013 {
13014 // Add the bulk elements
13015 tmp_boundary_element_pt.push_back(
13016 edge_element_pt[iproc][jproc][iiedge][0]);
13017 tmp_boundary_element_pt.push_back(
13018 edge_element_pt[iproc][jproc][iiedge][1]);
13019 // Add the face elements
13020 tmp_face_index_element.push_back(
13021 edge_element_face[iproc][jproc][iiedge][0]);
13022 tmp_face_index_element.push_back(
13023 edge_element_face[iproc][jproc][iiedge][1]);
13024 }
13025
13026 // Based on which side the node was added, look for
13027 // connections on that side
13028
13029 // Verify for connections to the left (we need to
13030 // check for the connection variable too, since
13031 // after a connection has been done we no longer
13032 // need to verify for this condition)
13033 if (node_added_to_the_left && !connection_to_the_left)
13034 {
13035 // Check for connection
13036 bound_id_connection_to_the_left =
13037 check_connections_of_polyline_nodes(
13038 element_in_processor_pt,
13039 root_edge_bound_id,
13040 overlapped_edge,
13041 node_on_bnd_not_overlapped_by_shd_bnd,
13042 sorted_nodes,
13043 shared_bnd_id_to_sorted_list_node_pt,
13044 added_node_degree,
13045 new_added_node_pt);
13046
13047 // If there is a connection then set the
13048 // corresponding flag
13049 // (-1): No connection
13050 // (-2): Connection with itself
13051 // (other value): Boundary id
13052 if (bound_id_connection_to_the_left != -1)
13053 {
13054 connection_to_the_left = true;
13055 } // if (bound_id_connection_to_the_left != -1)
13056
13057 } // if (node_added_to_the_left &&
13058 // !connection_to_the_left)
13059
13060 // Verify for connections to the right (we need to
13061 // check for the connection variable too, since
13062 // after a connection has been done we no longer
13063 // need to verify for this condition)
13064 if (node_added_to_the_right && !connection_to_the_right)
13065 {
13066 // Check for connection
13067 bound_id_connection_to_the_right =
13068 check_connections_of_polyline_nodes(
13069 element_in_processor_pt,
13070 root_edge_bound_id,
13071 overlapped_edge,
13072 node_on_bnd_not_overlapped_by_shd_bnd,
13073 sorted_nodes,
13074 shared_bnd_id_to_sorted_list_node_pt,
13075 added_node_degree,
13076 new_added_node_pt);
13077
13078 // If there is a connection then set the
13079 // corresponding flag
13080 // (-1): No connection
13081 // (-2): Connection with itself
13082 // (other value): Boundary id
13083 if (bound_id_connection_to_the_right != -1)
13084 {
13085 connection_to_the_right = true;
13086 } // if (bound_id_connection_to_the_right != -1)
13087
13088 } // if (node_added_to_the_right &&
13089 // !connection_to_the_right)
13090
13091 // If the current shared boundary has connections
13092 // at both ends then stop the adding of nodes
13093 if (connection_to_the_left && connection_to_the_right)
13094 {
13095 current_polyline_has_connections_at_both_ends = true;
13096 }
13097
13098 // Break the for and re-start to look more edges to
13099 // the left or right
13100 break;
13101
13102 } // if (node_added)
13103
13104 } // if (!edge_done[edge])
13105 } // for (iiedge < nedges)
13106
13107 } // while(node_added && (nsorted_edges < nedges)
13108 // && !current_polyline_has_connections_at_both_ends)
13109
13110 // ------------------------------------------------------------
13111 // If the sorted nodes of the shared polyline create a loop
13112 // it is necessary to break it by creating as many
13113 // polylines as required
13114
13115 // Change the list to a vector representation of the
13116 // boundary elements and the face indexes
13117
13118 // Get the number of boundary elements
13119 const unsigned n_bnd_ele = tmp_boundary_element_pt.size();
13120
13121 // Storage for the boundary elements and face indexes
13122 Vector<FiniteElement*> tmp_bnd_ele_pt(n_bnd_ele);
13123 Vector<int> tmp_face_idx_ele(n_bnd_ele);
13124 // Helper counter
13125 unsigned help_counter = 0;
13126 // Fill the data structures
13127 for (std::list<FiniteElement*>::iterator it_bnd_ele =
13128 tmp_boundary_element_pt.begin();
13129 it_bnd_ele != tmp_boundary_element_pt.end();
13130 it_bnd_ele++)
13131 {
13132 tmp_bnd_ele_pt[help_counter++] = (*it_bnd_ele);
13133 }
13134
13135 // Restart counter
13136 help_counter = 0;
13137 for (std::list<int>::iterator it_face_idx =
13138 tmp_face_index_element.begin();
13139 it_face_idx != tmp_face_index_element.end();
13140 it_face_idx++)
13141 {
13142 tmp_face_idx_ele[help_counter++] = (*it_face_idx);
13143 }
13144
13145 // Store the nodes for the new shared polylines without
13146 // loops
13147 Vector<std::list<Node*>> final_sorted_nodes_pt;
13148 // Store the boundary elements of the shared polyline
13149 // without loops
13150 Vector<Vector<FiniteElement*>> final_boundary_element_pt;
13151 // Face indexes of the boundary elements without loops
13152 Vector<Vector<int>> final_face_index_element;
13153 // Connection flags (to the left) of the shared boundaries
13154 // without loops
13155 Vector<int> final_bound_id_connection_to_the_left;
13156 // Connection flags (to the right) of the shared boundaries
13157 // without loops
13158 Vector<int> final_bound_id_connection_to_the_right;
13159
13160 // Break any possible loop created by the shared polyline
13161 break_loops_on_shared_polyline_helper(
13162 shared_boundary_id_start,
13163 sorted_nodes,
13164 tmp_bnd_ele_pt,
13165 tmp_face_idx_ele,
13166 bound_id_connection_to_the_left,
13167 bound_id_connection_to_the_right,
13168 final_sorted_nodes_pt,
13169 final_boundary_element_pt,
13170 final_face_index_element,
13171 final_bound_id_connection_to_the_left,
13172 final_bound_id_connection_to_the_right);
13173
13174 // Get the number of final sorted nodes
13175 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
13176
13177 // Loop over the list of final sorted nodes
13178 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
13179 {
13180 // --------------------------------------------------------
13181 // Associate the list of sorted nodes with the boundary id
13182 // of the shared boundary that is going to be crated
13183 shared_bnd_id_to_sorted_list_node_pt[shared_boundary_id_start] =
13184 final_sorted_nodes_pt[i];
13185
13186 // Create the shared polyline and fill the data
13187 // structured associated to it
13188 create_shared_polyline(my_rank,
13189 shared_boundary_id_start,
13190 iproc,
13191 jproc,
13192 final_sorted_nodes_pt[i],
13193 root_edge_bound_id,
13194 final_boundary_element_pt[i],
13195 final_face_index_element[i],
13196 unsorted_polylines_pt,
13197 final_bound_id_connection_to_the_left[i],
13198 final_bound_id_connection_to_the_right[i]);
13199
13200 // Increase the register for the number of created shared
13201 // polylines
13202 npolylines_counter++;
13203
13204 // Increase the boundary id (the one that will be used by
13205 // the next shared boundary)
13206 shared_boundary_id_start++;
13207
13208 } // for (i < n_final_sorted_nodes)
13209
13210 } // while(nsorted_edges < nedges);
13211
13212 } // for (jproc < nproc)
13213
13214 // We already have all the shared polylines (shared boundaries)
13215 // of processor iproc with processor jproc. Now we sort them so
13216 // that they be contiguous and can create polygons.
13217
13218 // If there are polylines to be sorted then sort them
13219 if (unsorted_polylines_pt[iproc].size() > 0)
13220 {
13221 // Now that we have all the new unsorted polylines on "iproc"
13222 // processor it is time to sort them so they be all contiguous
13223 sort_polylines_helper(unsorted_polylines_pt[iproc],
13224 output_polylines_pt[iproc]);
13225 }
13226
13227#ifdef PARANOID
13228 const unsigned nunsorted_polylines_iproc =
13229 unsorted_polylines_pt[iproc].size();
13230
13231 // Verify that all the polylines have been sorted
13232 unsigned tmp_ntotal_polylines = 0;
13233 // Count the total number of sorted polylines
13234 for (unsigned ii = 0; ii < output_polylines_pt[iproc].size(); ii++)
13235 {
13236 tmp_ntotal_polylines += output_polylines_pt[iproc][ii].size();
13237 }
13238 if (tmp_ntotal_polylines != nunsorted_polylines_iproc)
13239 {
13240 std::ostringstream error_message;
13241 error_message << " The total number of unsorted polylines ("
13242 << nunsorted_polylines_iproc
13243 << ") in common with\nprocessor (" << iproc
13244 << ") is different from the total number of sorted "
13245 << "polylines (" << tmp_ntotal_polylines
13246 << ") with\nthe same "
13247 << "proessor\n";
13248 throw OomphLibError(error_message.str(),
13249 OOMPH_CURRENT_FUNCTION,
13250 OOMPH_EXCEPTION_LOCATION);
13251 } // if (tmp_ntotal_polylines != nunsorted_polylines_iproc)
13252#endif
13253
13254 } // for (iproc < nproc)
13255
13256 // Establish the last used boundary id
13257 this->Final_shared_boundary_id = shared_boundary_id_start;
13258 }
13259
13260 // ======================================================================
13261 // Break any possible loop created by the sorted list of nodes
13262 // that is used to create a new shared polyline
13263 // ======================================================================
13264 template<class ELEMENT>
13266 const unsigned& initial_shd_bnd_id,
13267 std::list<Node*>& input_nodes,
13268 Vector<FiniteElement*>& input_boundary_element_pt,
13269 Vector<int>& input_face_index_element,
13270 const int& input_connect_to_the_left,
13271 const int& input_connect_to_the_right,
13272 Vector<std::list<Node*>>& output_sorted_nodes_pt,
13273 Vector<Vector<FiniteElement*>>& output_boundary_element_pt,
13274 Vector<Vector<int>>& output_face_index_element,
13275 Vector<int>& output_connect_to_the_left,
13276 Vector<int>& output_connect_to_the_right)
13277 {
13278 // Get the left and right node of the current list of sorted nodes
13279 Node* left_node_pt = input_nodes.front();
13280 Node* right_node_pt = input_nodes.back();
13281
13282 // Temporary storage for list of nodes, boundary elements and face
13283 // element's indexes
13284 Vector<std::list<Node*>> tmp_sub_nodes;
13285 Vector<Vector<FiniteElement*>> tmp_sub_bnd_ele_pt;
13286 Vector<Vector<int>> tmp_sub_face_idx_ele;
13287
13288 // Iterator for the list of input nodes
13289 std::list<Node*>::iterator it = input_nodes.begin();
13290
13291 // Counter
13292 unsigned counter = 0;
13293
13294 // Loop while not all nodes have been done
13295 while (it != input_nodes.end())
13296 {
13297 // Check if the current node is the final one
13298 it++;
13299 // Is the current node the final node?
13300 if (it == input_nodes.end())
13301 {
13302 // Break, add no more nodes
13303 break;
13304 }
13305 else
13306 {
13307 // Restore the iterator
13308 it--;
13309 }
13310
13311 // Get a list of nonrepeated nodes
13312 std::list<Node*> sub_nodes;
13313 // The temporary vector of boundary elements associated with the
13314 // nodes
13315 Vector<FiniteElement*> sub_bnd_ele_pt;
13316 // The temporary vector of face indexes associated with the
13317 // boundary elements
13318 Vector<int> sub_face_idx_ele;
13319
13320 // Add the current node to the list
13321 sub_nodes.push_back(*it);
13322
13323 // Add nodes until found a repeated node (the left or right
13324 // node) or until reaching the end of the list of nodes
13325 do
13326 {
13327 // Go to the next node
13328 ++it;
13329
13330 // Add the new node
13331 sub_nodes.push_back((*it));
13332
13333 // Add the boundary elements
13334 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter]);
13335 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter + 1]);
13336
13337 // Add the face indexes
13338 sub_face_idx_ele.push_back(input_face_index_element[counter]);
13339 sub_face_idx_ele.push_back(input_face_index_element[counter + 1]);
13340
13341 // Increase the counter
13342 counter += 2;
13343
13344 // Continue adding until reaching a repeated node or the end
13345 // of the list of nodes
13346 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
13347 it != input_nodes.end());
13348
13349 // Add the sub-set of nodes to the temporary storage
13350 tmp_sub_nodes.push_back(sub_nodes);
13351 // Add the face elements to the temporary storage
13352 tmp_sub_bnd_ele_pt.push_back(sub_bnd_ele_pt);
13353 // Add the face indexes to the temporary storage
13354 tmp_sub_face_idx_ele.push_back(sub_face_idx_ele);
13355
13356 } // while((*it) != input_nodes.end())
13357
13358 // --------------------------------------------------
13359 // Now create as many shared boundaries as required
13360
13361 // Get the number of sub-list of nodes created
13362 const unsigned n_sub_list = tmp_sub_nodes.size();
13363
13364#ifdef PARANOID
13365 if (n_sub_list > 3)
13366 {
13367 std::stringstream error_message;
13368 error_message
13369 << "The number of sub-list of nodes created from the shared\n"
13370 << "polyline with loops was (" << n_sub_list << ").\n"
13371 << "We can only handle up to three sub-list of nodes\n";
13372 throw OomphLibError(
13373 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
13374 }
13375#endif
13376
13377 // If there is only one list it may be because there are no loops or
13378 // there is only one loop (a circle)
13379 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
13380 {
13381 // There are no loops, return just after filling the data
13382 // structures
13383
13384 // This is the base case used most of the times
13385
13386 // Set the vector of lists of nodes
13387 output_sorted_nodes_pt = tmp_sub_nodes;
13388 // Set the vector of boundary elements
13389 output_boundary_element_pt = tmp_sub_bnd_ele_pt;
13390 // Set the vector of face indexes
13391 output_face_index_element = tmp_sub_face_idx_ele;
13392
13393 // Set the connection flags, change them by the proper connection
13394 // flag
13395
13396#ifdef PARANOID
13397 if (input_connect_to_the_left == -2)
13398 {
13399 std::stringstream error_message;
13400 error_message
13401 << "The connection flag to the left (" << input_connect_to_the_left
13402 << ") indicates a connection\n"
13403 << "with the same polyline.\n However, only one sub-polyline was "
13404 << "found and no loop\nwas identified\n\n";
13405 throw OomphLibError(error_message.str(),
13406 OOMPH_CURRENT_FUNCTION,
13407 OOMPH_EXCEPTION_LOCATION);
13408 }
13409#endif
13410
13411 // The left connection flag
13412 if (input_connect_to_the_left == -3)
13413 {
13414 output_connect_to_the_left.push_back(-1);
13415 }
13416 else
13417 {
13418 output_connect_to_the_left.push_back(input_connect_to_the_left);
13419 }
13420
13421#ifdef PARANOID
13422 if (input_connect_to_the_right == -2)
13423 {
13424 std::stringstream error_message;
13425 error_message
13426 << "The connection flag to the right (" << input_connect_to_the_right
13427 << ") indicates a connection\n"
13428 << "with the same polyline.\n However, only one sub-polyline was "
13429 << "found and no loop\nwas identified\n\n";
13430 throw OomphLibError(
13431 error_message.str(),
13432 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13433 OOMPH_EXCEPTION_LOCATION);
13434 }
13435#endif
13436
13437 // The right connection flag
13438 if (input_connect_to_the_right == -3)
13439 {
13440 output_connect_to_the_right.push_back(-1);
13441 }
13442 else
13443 {
13444 output_connect_to_the_right.push_back(input_connect_to_the_right);
13445 }
13446
13447 // Return inmediately
13448 return;
13449 }
13450
13451 // The temporary storage for the shared boundary id
13452 unsigned tmp_shd_bnd_id = initial_shd_bnd_id;
13453
13454 // -----------------------------------------------------------------
13455 // Check all the sub-list of nodes and create two shared boundaries
13456 // from those that make a loop (circle)
13457
13458 // -----------------------------------------------------------
13459 // Get the left and right node of the first sub-list of nodes
13460 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
13461 Node* right_sub_node_pt = tmp_sub_nodes[0].back();
13462
13463 // Check if the sub-list of nodes creates a loop (circle)
13464 if (left_sub_node_pt == right_sub_node_pt)
13465 {
13466 // We need to create two shared polylines and therefore increase
13467 // the shared boundary id by two
13468
13469 // The first and second half of nodes
13470 std::list<Node*> first_half_node_pt;
13471 std::list<Node*> second_half_node_pt;
13472 // The first and second half of boundary elements
13473 Vector<FiniteElement*> first_half_ele_pt;
13474 Vector<FiniteElement*> second_half_ele_pt;
13475 // The first and second half of face indexes
13476 Vector<int> first_half_face_idx;
13477 Vector<int> second_half_face_idx;
13478
13479 // Get the number of sub-nodes in the sub-list of nodes
13480 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
13481
13482 // The number of sub-nodes for the first half of the shared
13483 // boundary
13484 const unsigned n_sub_nodes_half =
13485 static_cast<unsigned>(n_sub_nodes / 2.0);
13486
13487 // Copy as many sub-nodes for the first half of the sub-polyline
13488
13489 // Iterator to loop over the nodes
13490 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
13491
13492 // Add the first node
13493 first_half_node_pt.push_back(*it_sub);
13494
13495 // Skip the first node
13496 it_sub++;
13497
13498 // Counter
13499 unsigned counter_nodes = 0;
13500 unsigned counter2 = 0;
13501
13502 // Loop to copy the nodes
13503 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13504 {
13505 // Add the sub-node to the first half
13506 first_half_node_pt.push_back(*it_sub);
13507
13508 // Add the boundary elements of the first half
13509 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
13510 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2 + 1]);
13511 // Add the face indexes of the first half
13512 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
13513 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2 + 1]);
13514
13515 // Increase the counter of added nodes
13516 counter_nodes++;
13517
13518 // Increase the other counter
13519 counter2 += 2;
13520
13521 if (counter_nodes == n_sub_nodes_half)
13522 {
13523 // Stop adding to the first half of nodes
13524 break;
13525 }
13526
13527 } // Copy the first half of nodes
13528
13529 // The second half
13530
13531 // Add the first node of the second half
13532 second_half_node_pt.push_back(*it_sub);
13533
13534 // Skip the first node of the second half
13535 it_sub++;
13536
13537 // Loop to copy the nodes
13538 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
13539 {
13540 // Add the sub-node to the first half
13541 second_half_node_pt.push_back(*it_sub);
13542
13543 // Add the boundary elements of the first half
13544 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
13545 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2 + 1]);
13546 // Add the face indexes of the first half
13547 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
13548 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2 + 1]);
13549
13550 // Increase the other counter
13551 counter2 += 2;
13552
13553 } // Copy the second half of nodes
13554
13555 // Add the sub-list of nodes to the vector of lists of nodes
13556 output_sorted_nodes_pt.push_back(first_half_node_pt);
13557 output_sorted_nodes_pt.push_back(second_half_node_pt);
13558 // Add the sub-vector of elements to the vector of boundary
13559 // elements
13560 output_boundary_element_pt.push_back(first_half_ele_pt);
13561 output_boundary_element_pt.push_back(second_half_ele_pt);
13562 // Add the sub-vector of face indexes to the vector of face
13563 // indexes
13564 output_face_index_element.push_back(first_half_face_idx);
13565 output_face_index_element.push_back(second_half_face_idx);
13566
13567 // Set the connection flags, change them by the proper connection
13568 // flag
13569
13570 // ----------------------------------------------------------------
13571 // Connections flags for the first half
13572
13573 // The left connection flag
13574
13575 // Connected with nothing but required to stop adding nodes
13576 if (input_connect_to_the_left == -3)
13577 {
13578 // Set connected to nothing
13579 output_connect_to_the_left.push_back(-1);
13580 }
13581 // Connected with itself
13582 else if (input_connect_to_the_left == -2)
13583 {
13584 // Set connected to nothing, this is the base node
13585 output_connect_to_the_left.push_back(-1);
13586 }
13587 else
13588 {
13589 // Any other value keep it
13590 output_connect_to_the_left.push_back(input_connect_to_the_left);
13591 }
13592
13593 // The right connection flag
13594
13595 // Set connected to nothing, this is the base node
13596 output_connect_to_the_right.push_back(-1);
13597
13598 // Increase the shared boundary id
13599 tmp_shd_bnd_id++;
13600
13601 // ----------------------------------------------------------------
13602 // Connections flags for the second half
13603
13604 // The left connection flag
13605
13606 // Set connected to the previous boundary
13607 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13608
13609 // The right connection flag
13610
13611 // Are we in the last sub-list of nodes, if that is the case we
13612 // need to respect the flag assigned to the right
13613 if (n_sub_list == 1)
13614 {
13615 if (input_connect_to_the_right == -3)
13616 {
13617 // Set connected to the previous shared boundary id
13618 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13619 }
13620 else if (input_connect_to_the_right == -2)
13621 {
13622 // Set connected to the previous shared boundary id
13623 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13624 }
13625 else if (input_connect_to_the_right == -1)
13626 {
13627 // Set connected to the previous shared boundary id
13628 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13629 }
13630 else
13631 {
13632 // Any other value keep it
13633 output_connect_to_the_right.push_back(input_connect_to_the_right);
13634 }
13635 } // if (n_sub_list == 1)
13636 else
13637 {
13638 // Set connected to the previous shared boundary id
13639 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13640 }
13641
13642 // Increase the shared boundary id
13643 tmp_shd_bnd_id++;
13644
13645 } // if (left_sub_node_pt == right_sub_node_pt)
13646 else
13647 {
13648 // No need to create two boundaries, create only one with the
13649 // sub-list of nodes
13650
13651 // Add the sub-list of nodes to the vector of lists of nodes
13652 output_sorted_nodes_pt.push_back(tmp_sub_nodes[0]);
13653 // Add the sub-vector of elements to the vector of boundary
13654 // elements
13655 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[0]);
13656 // Add the sub-vector of face indexes to the vector of face
13657 // indexes
13658 output_face_index_element.push_back(tmp_sub_face_idx_ele[0]);
13659
13660 // Set the connection flags, change them by the proper connection
13661 // flag
13662
13663 // The left connection flag
13664
13665 // Connected with nothing but required to stop adding nodes
13666 if (input_connect_to_the_left == -3)
13667 {
13668 // Set to connected to nothing
13669 output_connect_to_the_left.push_back(-1);
13670 }
13671 // Connected with itself
13672 else if (input_connect_to_the_left == -2)
13673 {
13674 // Set connected to the next shared polyline id
13675 output_connect_to_the_left.push_back(tmp_shd_bnd_id + 1);
13676 }
13677 else
13678 {
13679 // Any other value keep it
13680 output_connect_to_the_left.push_back(input_connect_to_the_left);
13681 }
13682
13683 // The right connection flag
13684
13685 // Set connected to the next shared polyline id
13686 output_connect_to_the_right.push_back(tmp_shd_bnd_id + 1);
13687
13688 // Increase the shared boundary id by one
13689 tmp_shd_bnd_id++;
13690
13691 } // else if (left_sub_node_pt == right_sub_node_pt)
13692
13693 // At least two sub-list of nodes were created
13694 if (n_sub_list > 1)
13695 {
13696 // ------------------------------------------------------------
13697 // Get the left and right node of the second sub-list of nodes
13698 left_sub_node_pt = tmp_sub_nodes[1].front();
13699 right_sub_node_pt = tmp_sub_nodes[1].back();
13700
13701 // Check if the sub-list of nodes creates a loop (circle)
13702 if (left_sub_node_pt == right_sub_node_pt)
13703 {
13704 // We need to create two shared polylines and therefore increase
13705 // the shared boundary id by two
13706
13707 // The first and second half of nodes
13708 std::list<Node*> first_half_node_pt;
13709 std::list<Node*> second_half_node_pt;
13710 // The first and second half of boundary elements
13711 Vector<FiniteElement*> first_half_ele_pt;
13712 Vector<FiniteElement*> second_half_ele_pt;
13713 // The first and second half of face indexes
13714 Vector<int> first_half_face_idx;
13715 Vector<int> second_half_face_idx;
13716
13717 // Get the number of sub-nodes in the sub-list of nodes
13718 const unsigned n_sub_nodes = tmp_sub_nodes[1].size();
13719
13720 // The number of sub-nodes for the first half of the shared
13721 // boundary
13722 const unsigned n_sub_nodes_half =
13723 static_cast<unsigned>(n_sub_nodes / 2.0);
13724
13725 // Copy as many sub-nodes for the first half of the sub-polyline
13726
13727 // Iterator to loop over the nodes
13728 std::list<Node*>::iterator it_sub = tmp_sub_nodes[1].begin();
13729
13730 // Add the first node
13731 first_half_node_pt.push_back(*it_sub);
13732
13733 // Skip the first node
13734 it_sub++;
13735
13736 // Counter
13737 unsigned counter_nodes = 0;
13738 unsigned counter2 = 0;
13739
13740 // Loop to copy the nodes
13741 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13742 {
13743 // Add the sub-node to the first half
13744 first_half_node_pt.push_back(*it_sub);
13745 // Add the boundary elements of the first half
13746 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2]);
13747 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2 + 1]);
13748 // Add the face indexes of the first half
13749 first_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2]);
13750 first_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2 + 1]);
13751
13752 // Increase the counter of added nodes
13753 counter_nodes++;
13754
13755 // Increase the other counter
13756 counter2 += 2;
13757
13758 if (counter_nodes == n_sub_nodes_half)
13759 {
13760 // Stop adding to the first half of nodes
13761 break;
13762 }
13763
13764 } // Copy the first half of nodes
13765
13766 // The second half
13767
13768 // Add the first node of the second half
13769 second_half_node_pt.push_back(*it_sub);
13770
13771 // Skip the first node of the second half
13772 it_sub++;
13773
13774 // Loop to copy the nodes
13775 for (; it_sub != tmp_sub_nodes[1].end(); it_sub++)
13776 {
13777 // Add the sub-node to the first half
13778 second_half_node_pt.push_back(*it_sub);
13779 // Add the boundary elements of the first half
13780 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2]);
13781 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[1][counter2 + 1]);
13782 // Add the face indexes of the first half
13783 second_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2]);
13784 second_half_face_idx.push_back(tmp_sub_face_idx_ele[1][counter2 + 1]);
13785
13786 // Increase the other counter
13787 counter2 += 2;
13788
13789 } // Copy the second half of nodes
13790
13791 // Add the sub-list of nodes to the vector of lists of nodes
13792 output_sorted_nodes_pt.push_back(first_half_node_pt);
13793 output_sorted_nodes_pt.push_back(second_half_node_pt);
13794 // Add the sub-vector of elements to the vector of boundary
13795 // elements
13796 output_boundary_element_pt.push_back(first_half_ele_pt);
13797 output_boundary_element_pt.push_back(second_half_ele_pt);
13798 // Add the sub-vector of face indexes to the vector of face
13799 // indexes
13800 output_face_index_element.push_back(first_half_face_idx);
13801 output_face_index_element.push_back(second_half_face_idx);
13802
13803 // Set the connection flags, change them by the proper
13804 // connection flag
13805
13806 // --------------------------------------
13807 // Connections flags for the first half
13808
13809 // The left connection flag
13810
13811 // Connected to the previous boundary
13812 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13813
13814 // The right connection flag
13815
13816 // Set connected to nothing, this is the base node
13817 output_connect_to_the_right.push_back(-1);
13818
13819 // Increase the shared boundary id
13820 tmp_shd_bnd_id++;
13821
13822 // --------------------------------------
13823 // Connections flags for the second half
13824
13825 // The left connection flag
13826
13827 // Set connected to the previous boundary
13828 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13829
13830 // The right connection flag
13831
13832 // Are we in the last sub-list of nodes, if that is the case we
13833 // need to respect the flag assigned to the right
13834 if (n_sub_list == 2)
13835 {
13836 // Connected with nothing
13837 if (input_connect_to_the_right == -1)
13838 {
13839 // Set connected to the previous shared boundary
13840 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13841 }
13842 // Connected with the same boundary
13843 else if (input_connect_to_the_right == -2)
13844 {
13845 // Set connected to the previous shared boundary
13846 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13847 }
13848 // Connetted with nothing but stop adding nodes
13849 else if (input_connect_to_the_right == -3)
13850 {
13851 // Set connected to the previous shared boundary
13852 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
13853 }
13854 else
13855 {
13856 // Any other value keep it
13857 output_connect_to_the_right.push_back(input_connect_to_the_right);
13858 }
13859
13860 // Increase the shared boundary id
13861 tmp_shd_bnd_id++;
13862
13863 } // if (n_sub_list == 2)
13864#ifdef PARANOID
13865 else
13866 {
13867 std::stringstream error_message;
13868 error_message
13869 << "The second sub-list of nodes creates a loop but this is not\n"
13870 << "the last list of sub-nodes.\n"
13871 << "This configuration is not supported\n";
13872 throw OomphLibError(
13873 error_message.str(),
13874 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13875 OOMPH_EXCEPTION_LOCATION);
13876 }
13877#endif
13878
13879 } // if (left_sub_node_pt == right_sub_node_pt)
13880 else
13881 {
13882 // No need to create two boundaries, create only one with the
13883 // sub-list of nodes
13884
13885 // Add the sub-list of nodes to the vector of lists of nodes
13886 output_sorted_nodes_pt.push_back(tmp_sub_nodes[1]);
13887 // Add the sub-vector of elements to the vector of boundary
13888 // elements
13889 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[1]);
13890 // Add the sub-vector of face indexes to the vector of face
13891 // indexes
13892 output_face_index_element.push_back(tmp_sub_face_idx_ele[1]);
13893
13894 // Set the connection flags, change them by the proper connection
13895 // flag
13896
13897 // The left connection flag
13898
13899 // Set connected to the previous shared boundary id
13900 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
13901
13902 // The right connection flag
13903
13904 // Are we in the last sub-list of nodes, if that is the case we
13905 // need to respect the flag assigned to the right
13906 if (n_sub_list == 2)
13907 {
13908 // Connected with nothing but required to stop adding nodes
13909 if (input_connect_to_the_right == -3)
13910 {
13911 // Set to connected to nothing
13912 output_connect_to_the_right.push_back(-1);
13913 }
13914#ifdef PARANOID
13915 // Connected with itself
13916 else if (input_connect_to_the_right == -2)
13917 {
13918 std::stringstream error_message;
13919 error_message
13920 << "The connection flag to the right ("
13921 << input_connect_to_the_right << ") indicates a connection\n"
13922 << "with the same polyline.\n However, the second sub-list of\n"
13923 << "nodes was found not making a loop so no connection with\n"
13924 << "itself should be marked\n\n";
13925 throw OomphLibError(
13926 error_message.str(),
13927 "TriangleMesh::break_loops_on_shared_polyline_helper()",
13928 OOMPH_EXCEPTION_LOCATION);
13929 }
13930#endif
13931 else
13932 {
13933 // Any other value keep it
13934 output_connect_to_the_right.push_back(input_connect_to_the_right);
13935 }
13936 } // if (n_sub_list == 2)
13937 else
13938 {
13939 // Set connected to the next shared boundary id
13940 output_connect_to_the_right.push_back(tmp_shd_bnd_id + 1);
13941 } // else if (n_sub_list == 2)
13942
13943 // Increase the shared boundary id by one
13944 tmp_shd_bnd_id++;
13945
13946 } // if (left_sub_node_pt == right_sub_node_pt)
13947
13948 } // if (n_sub_list > 1)
13949
13950 // Three sub-list of nodes were created
13951 if (n_sub_list > 2)
13952 {
13953 // ------------------------------------------------------------
13954 // Get the left and right node of the third sub-list of nodes
13955 left_sub_node_pt = tmp_sub_nodes[2].front();
13956 right_sub_node_pt = tmp_sub_nodes[2].back();
13957
13958 // Check if the sub-list of nodes creates a loop (circle)
13959 if (left_sub_node_pt == right_sub_node_pt)
13960 {
13961 // We need to create two shared polylines and therefore increase
13962 // the shared boundary id by two
13963
13964 // The first and second half of nodes
13965 std::list<Node*> first_half_node_pt;
13966 std::list<Node*> second_half_node_pt;
13967 // The first and second half of boundary elements
13968 Vector<FiniteElement*> first_half_ele_pt;
13969 Vector<FiniteElement*> second_half_ele_pt;
13970 // The first and second half of face indexes
13971 Vector<int> first_half_face_idx;
13972 Vector<int> second_half_face_idx;
13973
13974 // Get the number of sub-nodes in the sub-list of nodes
13975 const unsigned n_sub_nodes = tmp_sub_nodes[2].size();
13976
13977 // The number of sub-nodes for the first half of the shared
13978 // boundary
13979 const unsigned n_sub_nodes_half =
13980 static_cast<unsigned>(n_sub_nodes / 2.0);
13981
13982 // Copy as many sub-nodes for the first half of the sub-polyline
13983
13984 // Iterator to loop over the nodes
13985 std::list<Node*>::iterator it_sub = tmp_sub_nodes[2].begin();
13986
13987 // Add the first node
13988 first_half_node_pt.push_back(*it_sub);
13989
13990 // Skip the first node
13991 it_sub++;
13992
13993 // Counter
13994 unsigned counter_nodes = 0;
13995 unsigned counter2 = 0;
13996
13997 // Loop to copy the nodes
13998 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
13999 {
14000 // Add the sub-node to the first half
14001 first_half_node_pt.push_back(*it_sub);
14002 // Add the boundary elements of the first half
14003 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2]);
14004 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2 + 1]);
14005 // Add the face indexes of the first half
14006 first_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2]);
14007 first_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2 + 1]);
14008
14009 // Increase the counter of added nodes
14010 counter_nodes++;
14011
14012 // Increase the other counter
14013 counter2 += 2;
14014
14015 if (counter_nodes == n_sub_nodes_half)
14016 {
14017 // Stop adding to the first half of nodes
14018 break;
14019 }
14020
14021 } // Copy the first half of nodes
14022
14023 // The second half
14024
14025 // Add the first node of the second half
14026 second_half_node_pt.push_back(*it_sub);
14027
14028 // Skip the first node of the second half
14029 it_sub++;
14030
14031 // Loop to copy the nodes
14032 for (; it_sub != tmp_sub_nodes[2].end(); it_sub++)
14033 {
14034 // Add the sub-node to the first half
14035 second_half_node_pt.push_back(*it_sub);
14036 // Add the boundary elements of the first half
14037 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2]);
14038 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[2][counter2 + 1]);
14039 // Add the face indexes of the first half
14040 second_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2]);
14041 second_half_face_idx.push_back(tmp_sub_face_idx_ele[2][counter2 + 1]);
14042
14043 // Increase the other counter
14044 counter2 += 2;
14045
14046 } // Copy the second half of nodes
14047
14048 // Add the sub-list of nodes to the vector of lists of nodes
14049 output_sorted_nodes_pt.push_back(first_half_node_pt);
14050 output_sorted_nodes_pt.push_back(second_half_node_pt);
14051 // Add the sub-vector of elements to the vector of boundary
14052 // elements
14053 output_boundary_element_pt.push_back(first_half_ele_pt);
14054 output_boundary_element_pt.push_back(second_half_ele_pt);
14055 // Add the sub-vector of face indexes to the vector of face
14056 // indexes
14057 output_face_index_element.push_back(first_half_face_idx);
14058 output_face_index_element.push_back(second_half_face_idx);
14059
14060 // --------------------------------------
14061 // Connections flags for the first half
14062
14063 // The left connection flag
14064
14065 // Connected to the previous shared boundary
14066 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14067
14068 // The right connection flag
14069
14070 // Set connected to nothing, this is the base node
14071 output_connect_to_the_right.push_back(-1);
14072
14073 // Increase the shared boundary id
14074 tmp_shd_bnd_id++;
14075
14076 // --------------------------------------
14077 // Connections flags for the second half
14078
14079 // The left connection flag
14080
14081 // Set connected to the previous boundary
14082 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14083
14084 // The right connection flag
14085
14086 if (input_connect_to_the_right == -3)
14087 {
14088 // Set connected to the previous shared boundary id
14089 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14090 }
14091 else if (input_connect_to_the_right == -2)
14092 {
14093 // Set connected to the previous shared boundary id
14094 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14095 }
14096 else if (input_connect_to_the_right == -1)
14097 {
14098 // Set connected to the previous shared boundary id
14099 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14100 }
14101 else
14102 {
14103 // Any other value keep it
14104 output_connect_to_the_right.push_back(input_connect_to_the_right);
14105 }
14106
14107 // Increase the shared boundary id
14108 tmp_shd_bnd_id++;
14109
14110 } // if (left_sub_node_pt == right_sub_node_pt)
14111 else
14112 {
14113 // No need to create two boundaries, create only one with the
14114 // sub-list of nodes
14115
14116 // Add the sub-list of nodes to the vector of lists of nodes
14117 output_sorted_nodes_pt.push_back(tmp_sub_nodes[2]);
14118 // Add the sub-vector of elements to the vector of boundary
14119 // elements
14120 output_boundary_element_pt.push_back(tmp_sub_bnd_ele_pt[2]);
14121 // Add the sub-vector of face indexes to the vector of face
14122 // indexes
14123 output_face_index_element.push_back(tmp_sub_face_idx_ele[2]);
14124
14125 // Set the connection flags, change them by the proper
14126 // connection flag
14127
14128 // The left connection flag
14129
14130 // Set connected to the previous shared boundary id
14131 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14132
14133 // The right connection flag
14134
14135 // Connected with nothing but required to stop adding nodes
14136 if (input_connect_to_the_right == -3)
14137 {
14138 std::stringstream error_message;
14139 error_message
14140 << "The connection flag to the right ("
14141 << input_connect_to_the_right << ") indicates 'no connection and\n"
14142 << "stop adding nodes'.\n However, the thrid sub-list of\n"
14143 << "nodes must have a connection to the right with the same\n"
14144 << "shared polyline or with any other polyline\n\n";
14145 throw OomphLibError(
14146 error_message.str(),
14147 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14148 OOMPH_EXCEPTION_LOCATION);
14149 }
14150 else if (input_connect_to_the_right == -1)
14151 {
14152 std::stringstream error_message;
14153 error_message
14154 << "The connection flag to the right ("
14155 << input_connect_to_the_right << ") indicates 'no connection.\n"
14156 << "However, the thrid sub-list of nodes must have a connection\n"
14157 << "to the right with the same shared polyline or with any other\n"
14158 << "polyline\n\n";
14159 throw OomphLibError(
14160 error_message.str(),
14161 "TriangleMesh::break_loops_on_shared_polyline_helper()",
14162 OOMPH_EXCEPTION_LOCATION);
14163 }
14164 // Connected with itself
14165 else if (input_connect_to_the_right == -2)
14166 {
14167 // Set connected to the previous shared boundary id
14168 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14169 }
14170 else
14171 {
14172 // Any other value keep it
14173 output_connect_to_the_right.push_back(input_connect_to_the_right);
14174 }
14175
14176 // Increase the shared boundary id by one
14177 tmp_shd_bnd_id++;
14178
14179 } // if (left_sub_node_pt == right_sub_node_pt)
14180
14181 } // if (n_sub_list > 2)
14182 }
14183
14184 // ======================================================================
14185 // Break any possible loop created by the sorted list of nodes
14186 // that is used to create a new shared polyline
14187 // ======================================================================
14188 template<class ELEMENT>
14191 const unsigned& initial_shd_bnd_id,
14192 std::list<Node*>& input_nodes,
14193 Vector<FiniteElement*>& input_boundary_element_pt,
14194 Vector<FiniteElement*>& input_boundary_face_element_pt,
14195 Vector<int>& input_face_index_element,
14196 const int& input_connect_to_the_left,
14197 const int& input_connect_to_the_right,
14198 Vector<std::list<Node*>>& output_sorted_nodes_pt,
14199 Vector<Vector<FiniteElement*>>& output_boundary_element_pt,
14200 Vector<Vector<FiniteElement*>>& output_boundary_face_element_pt,
14201 Vector<Vector<int>>& output_face_index_element,
14202 Vector<int>& output_connect_to_the_left,
14203 Vector<int>& output_connect_to_the_right)
14204 {
14205 // Get the left and right node of the current list of sorted nodes
14206 Node* left_node_pt = input_nodes.front();
14207 Node* right_node_pt = input_nodes.back();
14208
14209 // Temporary storage for list of nodes, boundary elements, boundary
14210 // face elements and face element's indexes
14211 Vector<std::list<Node*>> tmp_sub_nodes;
14212 Vector<Vector<FiniteElement*>> tmp_sub_bnd_ele_pt;
14213 Vector<Vector<FiniteElement*>> tmp_sub_bnd_face_ele_pt;
14214 Vector<Vector<int>> tmp_sub_face_idx_ele;
14215
14216 // Iterator for the list of input nodes
14217 std::list<Node*>::iterator it = input_nodes.begin();
14218
14219 // Counter
14220 unsigned counter = 0;
14221
14222 // Loop while not all nodes have been done
14223 while (it != input_nodes.end())
14224 {
14225 // Check if the current node is the final one
14226 it++;
14227 // Is the current node the final node?
14228 if (it == input_nodes.end())
14229 {
14230 // Break, add no more nodes
14231 break;
14232 }
14233 else
14234 {
14235 // Restore the iterator
14236 it--;
14237 }
14238
14239 // Get a list of nonrepeated nodes
14240 std::list<Node*> sub_nodes;
14241 // The temporary vector of boundary elements associated with the
14242 // nodes
14243 Vector<FiniteElement*> sub_bnd_ele_pt;
14244 // The temporary vector of boundary face elements associated with
14245 // the nodes
14246 Vector<FiniteElement*> sub_bnd_face_ele_pt;
14247 // The temporary vector of face indexes associated with the
14248 // boundary elements
14249 Vector<int> sub_face_idx_ele;
14250
14251 // Add the current node to the list
14252 sub_nodes.push_back(*it);
14253
14254 // Add nodes until found a repeated node (the left or right
14255 // node) or until reaching the end of the list of nodes
14256 do
14257 {
14258 // Go to the next node
14259 ++it;
14260
14261 // Add the new node
14262 sub_nodes.push_back((*it));
14263
14264 // Add the boundary elements
14265 sub_bnd_ele_pt.push_back(input_boundary_element_pt[counter]);
14266
14267 // Add the boundary face elements
14268 sub_bnd_face_ele_pt.push_back(input_boundary_face_element_pt[counter]);
14269
14270 // Add the face indexes
14271 sub_face_idx_ele.push_back(input_face_index_element[counter]);
14272
14273 // Increase the counter
14274 counter++;
14275
14276 // Continue adding until reaching a repeated node or the end
14277 // of the list of nodes
14278 } while ((*it) != left_node_pt && (*it) != right_node_pt &&
14279 it != input_nodes.end());
14280
14281 // Add the sub-set of nodes to the temporary storage
14282 tmp_sub_nodes.push_back(sub_nodes);
14283
14284 // Add the boundary elements to the temporary storage
14285 tmp_sub_bnd_ele_pt.push_back(sub_bnd_ele_pt);
14286 // Add the boundary face elements to the temporary storage
14287 tmp_sub_bnd_face_ele_pt.push_back(sub_bnd_face_ele_pt);
14288 // Add the face indexes to the temporary storage
14289 tmp_sub_face_idx_ele.push_back(sub_face_idx_ele);
14290
14291 } // while((*it) != input_nodes.end())
14292
14293 // --------------------------------------------------
14294 // Now create as many shared boundaries as required
14295
14296 // Get the number of sub-list of nodes created
14297 const unsigned n_sub_list = tmp_sub_nodes.size();
14298
14299#ifdef PARANOID
14300 if (n_sub_list > 1)
14301 {
14302 std::stringstream error_message;
14303 error_message
14304 << "The number of sub-list of nodes created from the shared\n"
14305 << "polyline with loops was (" << n_sub_list << ").\n"
14306 << "We can only handle one list which may still contain loops\n"
14307 << "(or repeated nodes)\n";
14308 throw OomphLibError(
14309 error_message.str(),
14310 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14311 OOMPH_EXCEPTION_LOCATION);
14312 }
14313#endif
14314
14315 // If there is only one list it may be because there are no loops or
14316 // there is only one loop (a circle)
14317 if (n_sub_list == 1 && (left_node_pt != right_node_pt))
14318 {
14319 // There are no loops, return just after filling the data
14320 // structures
14321
14322 // This is the base case used most of the times
14323
14324 // Set the vector of lists of nodes
14325 output_sorted_nodes_pt = tmp_sub_nodes;
14326 // Set the vector of boundary elements
14327 output_boundary_element_pt = tmp_sub_bnd_ele_pt;
14328 // Set the vector of boundary face elements
14329 output_boundary_face_element_pt = tmp_sub_bnd_face_ele_pt;
14330 // Set the vector of face indexes
14331 output_face_index_element = tmp_sub_face_idx_ele;
14332
14333 // Set the connection flags, change them by the proper connection
14334 // flag
14335
14336#ifdef PARANOID
14337 if (input_connect_to_the_left == -2)
14338 {
14339 std::stringstream error_message;
14340 error_message
14341 << "The connection flag to the left (" << input_connect_to_the_left
14342 << ") indicates a connection\n"
14343 << "with the same polyline.\n However, only one sub-polyline was "
14344 << "found and no loops\nwere identified\n\n";
14345 throw OomphLibError(
14346 error_message.str(),
14347 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14348 OOMPH_EXCEPTION_LOCATION);
14349 }
14350#endif
14351
14352 // The left connection flag
14353 if (input_connect_to_the_left == -3)
14354 {
14355 output_connect_to_the_left.push_back(-1);
14356 }
14357 else
14358 {
14359 output_connect_to_the_left.push_back(input_connect_to_the_left);
14360 }
14361
14362#ifdef PARANOID
14363 if (input_connect_to_the_right == -2)
14364 {
14365 std::stringstream error_message;
14366 error_message
14367 << "The connection flag to the right (" << input_connect_to_the_right
14368 << ") indicates a connection\n"
14369 << "with the same polyline.\n However, only one sub-polyline was "
14370 << "found and no loops\nwere identified\n\n";
14371 throw OomphLibError(
14372 error_message.str(),
14373 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14374 OOMPH_EXCEPTION_LOCATION);
14375 }
14376#endif
14377
14378 // The right connection flag
14379 if (input_connect_to_the_right == -3)
14380 {
14381 output_connect_to_the_right.push_back(-1);
14382 }
14383 else
14384 {
14385 output_connect_to_the_right.push_back(input_connect_to_the_right);
14386 }
14387
14388 // Return immediately
14389 return;
14390 }
14391
14392 // The temporary storage for the shared boundary id
14393 unsigned tmp_shd_bnd_id = initial_shd_bnd_id;
14394
14395 // -----------------------------------------------------------------
14396 // Check all the sub-list of nodes and create two shared boundaries
14397 // from those that make a loop (circle)
14398
14399 // -----------------------------------------------------------
14400 // Get the left and right node of the first sub-list of nodes
14401 Node* left_sub_node_pt = tmp_sub_nodes[0].front();
14402 Node* right_sub_node_pt = tmp_sub_nodes[0].back();
14403
14404 // Check if the sub-list of nodes creates a loop (circle)
14405 if (left_sub_node_pt == right_sub_node_pt)
14406 {
14407 // We need to create two shared polylines and therefore increase
14408 // the shared boundary id by two
14409
14410 // The first and second half of nodes
14411 std::list<Node*> first_half_node_pt;
14412 std::list<Node*> second_half_node_pt;
14413 // The first and second half of boundary elements
14414 Vector<FiniteElement*> first_half_ele_pt;
14415 Vector<FiniteElement*> second_half_ele_pt;
14416 // The first and second half of boundary face elements
14417 Vector<FiniteElement*> first_half_ele_face_pt;
14418 Vector<FiniteElement*> second_half_ele_face_pt;
14419 // The first and second half of face indexes
14420 Vector<int> first_half_face_idx;
14421 Vector<int> second_half_face_idx;
14422
14423 // Get the number of sub-nodes in the sub-list of nodes
14424 const unsigned n_sub_nodes = tmp_sub_nodes[0].size();
14425
14426 // The number of sub-nodes for the first half of the shared
14427 // boundary
14428 const unsigned n_sub_nodes_half =
14429 static_cast<unsigned>(n_sub_nodes / 2.0);
14430
14431 // Copy as many sub-nodes for the first half of the sub-polyline
14432
14433 // Iterator to loop over the nodes
14434 std::list<Node*>::iterator it_sub = tmp_sub_nodes[0].begin();
14435
14436 // Add the first node
14437 first_half_node_pt.push_back(*it_sub);
14438
14439 // Skip the first node
14440 it_sub++;
14441
14442 // Counter
14443 unsigned counter_nodes = 0;
14444 unsigned counter2 = 0;
14445
14446 // Loop to copy the nodes
14447 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14448 {
14449 // Add the sub-node to the first half
14450 first_half_node_pt.push_back(*it_sub);
14451
14452 // Add the boundary elements of the first half
14453 first_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
14454 // Add the boundary face elements of the first half
14455 first_half_ele_face_pt.push_back(tmp_sub_bnd_face_ele_pt[0][counter2]);
14456 // Add the face indexes of the first half
14457 first_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
14458
14459 // Increase the counter of added nodes
14460 counter_nodes++;
14461
14462 // Increase the other counter (of the elements/face)
14463 counter2++;
14464
14465 if (counter_nodes == n_sub_nodes_half)
14466 {
14467 // Stop adding to the first half of nodes
14468 break;
14469 }
14470
14471 } // Copy the first half of nodes
14472
14473 // The second half
14474
14475 // Add the first node of the second half
14476 second_half_node_pt.push_back(*it_sub);
14477
14478 // Skip the first node of the second half
14479 it_sub++;
14480
14481 // Loop to copy the nodes
14482 for (; it_sub != tmp_sub_nodes[0].end(); it_sub++)
14483 {
14484 // Add the sub-node to the first half
14485 second_half_node_pt.push_back(*it_sub);
14486
14487 // Add the boundary elements of the first half
14488 second_half_ele_pt.push_back(tmp_sub_bnd_ele_pt[0][counter2]);
14489 // Add the boundary face elements of the first half
14490 second_half_ele_face_pt.push_back(tmp_sub_bnd_face_ele_pt[0][counter2]);
14491 // Add the face indexes of the first half
14492 second_half_face_idx.push_back(tmp_sub_face_idx_ele[0][counter2]);
14493
14494 // Increase the other counter
14495 counter2++;
14496
14497 } // Copy the second half of nodes
14498
14499 // Add the sub-list of nodes to the vector of lists of nodes
14500 output_sorted_nodes_pt.push_back(first_half_node_pt);
14501 output_sorted_nodes_pt.push_back(second_half_node_pt);
14502 // Add the sub-vector of elements to the vector of boundary
14503 // elements
14504 output_boundary_element_pt.push_back(first_half_ele_pt);
14505 output_boundary_element_pt.push_back(second_half_ele_pt);
14506 // Add the sub-vector of face elements to the vector of boundary
14507 // elements
14508 output_boundary_face_element_pt.push_back(first_half_ele_face_pt);
14509 output_boundary_face_element_pt.push_back(second_half_ele_face_pt);
14510 // Add the sub-vector of face indexes to the vector of face
14511 // indexes
14512 output_face_index_element.push_back(first_half_face_idx);
14513 output_face_index_element.push_back(second_half_face_idx);
14514
14515 // Set the connection flags, change them by the proper connection
14516 // flag
14517
14518 // ----------------------------------------------------------------
14519 // Connections flags for the first half
14520
14521 // The left connection flag
14522
14523 // Connected with nothing but required to stop adding nodes
14524 if (input_connect_to_the_left == -3)
14525 {
14526 // Set connected to nothing
14527 output_connect_to_the_left.push_back(-1);
14528 }
14529 // Connected with itself
14530 else if (input_connect_to_the_left == -2)
14531 {
14532 // Set connected to nothing, this is the base node
14533 output_connect_to_the_left.push_back(-1);
14534 }
14535 else
14536 {
14537 // Any other value keep it
14538 output_connect_to_the_left.push_back(input_connect_to_the_left);
14539 }
14540
14541 // The right connection flag
14542
14543 // Set connected to nothing, this is the base node
14544 output_connect_to_the_right.push_back(-1);
14545
14546 // Increase the shared boundary id
14547 tmp_shd_bnd_id++;
14548
14549 // ----------------------------------------------------------------
14550 // Connections flags for the second half
14551
14552 // The left connection flag
14553
14554 // Set connected to the previous boundary
14555 output_connect_to_the_left.push_back(tmp_shd_bnd_id - 1);
14556
14557 // The right connection flag
14558
14559 // Are we in the last sub-list of nodes, if that is the case we
14560 // need to respect the flag assigned to the right
14561 if (n_sub_list == 1)
14562 {
14563 if (input_connect_to_the_right == -3)
14564 {
14565 // Set connected to the previous shared boundary id
14566 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14567 }
14568 else if (input_connect_to_the_right == -2)
14569 {
14570 // Set connected to the previous shared boundary id
14571 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14572 }
14573 else if (input_connect_to_the_right == -1)
14574 {
14575 // Set connected to the previous shared boundary id
14576 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14577 }
14578 else
14579 {
14580 // Any other value keep it
14581 output_connect_to_the_right.push_back(input_connect_to_the_right);
14582 }
14583 } // if (n_sub_list == 1)
14584 else
14585 {
14586 // Set connected to the previous shared boundary id
14587 output_connect_to_the_right.push_back(tmp_shd_bnd_id - 1);
14588 }
14589
14590 // Increase the shared boundary id
14591 tmp_shd_bnd_id++;
14592
14593 } // if (left_sub_node_pt == right_sub_node_pt)
14594#ifdef PARANOID
14595 else
14596 {
14597 std::stringstream error_message;
14598 error_message
14599 << "The initial and final node in the current shared polyline are not\n"
14600 << "the same and the number of sublists is (" << n_sub_list << ").\n"
14601 << "We can not handle more than one sublist in the method to break\n"
14602 << "loops at the load balance stage\n\n";
14603 throw OomphLibError(
14604 error_message.str(),
14605 "TriangleMesh::break_loops_on_shared_polyline_load_balance_helper()",
14606 OOMPH_EXCEPTION_LOCATION);
14607 }
14608#endif
14609 }
14610
14611 // ======================================================================
14612 // Create the shared polyline and fill the data structured
14613 // that keep all the information associated with the creationg of the
14614 // shared boundary
14615 // ======================================================================
14616 template<class ELEMENT>
14618 const unsigned& my_rank,
14619 const unsigned& shd_bnd_id,
14620 const unsigned& iproc,
14621 const unsigned& jproc,
14622 std::list<Node*>& sorted_nodes,
14623 const int& root_edge_bnd_id,
14624 Vector<FiniteElement*>& bulk_bnd_ele_pt,
14625 Vector<int>& face_index_ele,
14626 Vector<Vector<TriangleMeshPolyLine*>>& unsorted_polylines_pt,
14627 const int& connect_to_the_left_flag,
14628 const int& connect_to_the_right_flag)
14629 {
14630 // ----------------------------------------------------------------
14631 // Associate the shared boundary with the respective processors
14632 // ----------------------------------------------------------------
14633
14634 // Setup the global look-up scheme, where all processors know the
14635 // associations of others processors and the shared boundaries they
14636 // created
14637
14638 // Set up the boundary shared by "iproc" with "jproc" processor
14639 Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
14640
14641 // Set up the boundary shared by "jproc" with "iproc" processor
14642 Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
14643
14644 // Specify the processors involved on the creation of the shared
14645 // boundary
14646 Vector<unsigned> processors(2);
14647 processors[0] = iproc;
14648 processors[1] = jproc;
14649 Shared_boundary_from_processors[shd_bnd_id] = processors;
14650
14651 // ----------------------------------------------------------------
14652 // If one of the processor associated with the shared boundary is
14653 // the current processor then it needs to create a polyline from the
14654 // input sorted nodes, other processors can skip this part
14655 if (iproc == my_rank || jproc == my_rank)
14656 {
14657 // ------------------------------------------------------------
14658 // Create a vertices representation from the sorted nodes list
14659 // ------------------------------------------------------------
14660
14661 // Get the number of nodes on the list
14662 const unsigned n_nodes = sorted_nodes.size();
14663 // The vector to store the vertices (assign space)
14664 Vector<Vector<double>> vertices(n_nodes);
14665
14666 // Copy the vertices from the nodes
14667 unsigned counter = 0;
14668
14669 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14670 it != sorted_nodes.end();
14671 it++)
14672 {
14673 vertices[counter].resize(2);
14674 vertices[counter][0] = (*it)->x(0);
14675 vertices[counter][1] = (*it)->x(1);
14676 counter++;
14677 }
14678
14679 // ---------------------------------------------
14680 // Create the polyline from the input vertices
14681 // ---------------------------------------------
14682 TriangleMeshPolyLine* polyline_pt =
14683 new TriangleMeshPolyLine(vertices, shd_bnd_id);
14684
14685 // ---------------------------------------------
14686 // Establish the internal boundary information
14687 // ---------------------------------------------
14688
14689 // Check if the shared boundary is overlapping (or is part) of an
14690 // internal boundary
14691 if (root_edge_bnd_id != -1)
14692 {
14693 // If the shared boundary is part of an internal boundary then
14694 // mark the shared boundary
14695 Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
14696 static_cast<unsigned>(root_edge_bnd_id);
14697 } // if (root_edge_bnd_id != -1)
14698
14699 // ---------------------------------------------
14700 // Store the boundary elements and face indexes
14701 // ---------------------------------------------
14702
14703 // Store the shared boundary elements
14704 const unsigned n_shared_boundary_elements = bulk_bnd_ele_pt.size();
14705#ifdef PARANOID
14706 // Check that the number of shared boundy elements is the same as
14707 // the number of face indexes
14708 const unsigned n_face_index = face_index_ele.size();
14709 if (n_shared_boundary_elements != n_face_index)
14710 {
14711 std::ostringstream error_message;
14712 error_message
14713 << "The number of shared boundary elements is different from the\n"
14714 << "number of face indexes associated to the shared boundary\n"
14715 << "elements\n"
14716 << "Number of shared boundary elements: ("
14717 << n_shared_boundary_elements << ")\n"
14718 << "Number of face indexes: (" << n_face_index << ")\n\n";
14719 throw OomphLibError(error_message.str(),
14720 "TriangleMesh::create_shared_polyline()",
14721 OOMPH_EXCEPTION_LOCATION);
14722 } // if (n_shared_boundary_elements != n_face_index)
14723#endif
14724
14725 // Add the shared boundary elements and their respective face
14726 // indexes to their permanent containers
14727 for (unsigned i = 0; i < n_shared_boundary_elements; i++)
14728 {
14729 add_shared_boundary_element(shd_bnd_id, bulk_bnd_ele_pt[i]);
14730 add_face_index_at_shared_boundary(shd_bnd_id, face_index_ele[i]);
14731 } // for (i < nshared_boundary_elements)
14732
14733 // Store the shared boundary nodes
14734 for (std::list<Node*>::iterator it = sorted_nodes.begin();
14735 it != sorted_nodes.end();
14736 it++)
14737 {
14738 add_shared_boundary_node(shd_bnd_id, (*it));
14739 } // for (it != sorted_nodes.end())
14740
14741 // ----------------------------------------------------------
14742 // Create additional look-up schemes for the shared boundary
14743 // ----------------------------------------------------------
14744
14745 // Updates bnd_id <---> curve section map
14746 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
14747
14748 // Check the size of the unsorted_polylines_pt structure. This
14749 // will have n_procs = 1 when it was called from the
14750 // create_new_shared_boundaries() methods
14751 const unsigned n_procs = unsorted_polylines_pt.size();
14752 if (n_procs > 1)
14753 {
14754 // Add the new created polyline to the list of unsorted
14755 // polylines
14756 unsorted_polylines_pt[iproc].push_back(polyline_pt);
14757
14758 // ... do this on both processors involved in the creation of
14759 // the shared boundary
14760 unsorted_polylines_pt[jproc].push_back(polyline_pt);
14761 }
14762 else
14763 {
14764 // Add the new created polyline to the list of unsorted
14765 // polylines
14766 unsorted_polylines_pt[0].push_back(polyline_pt);
14767 }
14768
14769 // Mark the polyline for deletion (when calling destructor)
14770 this->Free_curve_section_pt.insert(polyline_pt);
14771
14772 // ----------------------------
14773 // Set connection information
14774 // ----------------------------
14775
14776 // Check that the flags are correct, no connection or the boundary
14777 // id of the boundary to connect
14778#ifdef PARANOID
14779 // Is the shared polyline not connected to the left
14780 if (connect_to_the_left_flag < 0)
14781 {
14782 // If not connected then should be specified by -1
14783 if (connect_to_the_left_flag != -1)
14784 {
14785 std::ostringstream error_message;
14786 error_message
14787 << "The only accepted values for the connection flags are:\n"
14788 << "POSITIVE values or -1, any other value is rejected, please\n"
14789 << "check that you previously called the methods to deal with\n"
14790 << "other flag values\n"
14791 << "The current flag value for connection to the left is: ("
14792 << connect_to_the_left_flag << ")\n\n";
14793 throw OomphLibError(error_message.str(),
14794 "TriangleMesh::create_shared_polyline()",
14795 OOMPH_EXCEPTION_LOCATION);
14796 } // if (connect_to_the_left_flag != -1)
14797 } // if (connect_to_the_left_flag < 0)
14798
14799 // Is the shared polyline not connected to the right
14800 if (connect_to_the_right_flag < 0)
14801 {
14802 // If not connected then should be specified by -1
14803 if (connect_to_the_right_flag != -1)
14804 {
14805 std::ostringstream error_message;
14806 error_message
14807 << "The only accepted values for the connection flags are:\n"
14808 << "POSITIVE values or -1, any other value is rejected, please\n"
14809 << "check that you previously called the methods to deal with\n"
14810 << "other flag values\n"
14811 << "The current flag value for connection to the right is: ("
14812 << connect_to_the_right_flag << ")\n\n";
14813 throw OomphLibError(error_message.str(),
14814 "TriangleMesh::create_shared_polyline()",
14815 OOMPH_EXCEPTION_LOCATION);
14816 } // if (connect_to_the_right_flag != -1)
14817 } // if (connect_to_the_right_flag < 0)
14818#endif
14819
14820 // Set the connection to the left
14821 if (connect_to_the_left_flag != -1)
14822 {
14823 // Get the unsigned version of the boundary id to the left
14824 const unsigned bnd_id_connection_to_the_left =
14825 static_cast<unsigned>(connect_to_the_left_flag);
14826 // Set the initial vertex as connected
14827 polyline_pt->set_initial_vertex_connected();
14828 // Set the initial vertex connected boundary id
14829 polyline_pt->initial_vertex_connected_bnd_id() =
14830 bnd_id_connection_to_the_left;
14831 // Set the chunk number to zero
14832 polyline_pt->initial_vertex_connected_n_chunk() = 0;
14833
14834 } // if (connect_to_the_left_flag != -1)
14835
14836 // Set the connection to the right
14837 if (connect_to_the_right_flag != -1)
14838 {
14839 // Get the unsigned version of the boundary id to the right
14840 const unsigned bnd_id_connection_to_the_right =
14841 static_cast<unsigned>(connect_to_the_right_flag);
14842 // Set the final vertex as connected
14843 polyline_pt->set_final_vertex_connected();
14844 // Set the final vertex connected boundary id
14845 polyline_pt->final_vertex_connected_bnd_id() =
14846 bnd_id_connection_to_the_right;
14847 // Set the chunk number to zero
14848 polyline_pt->final_vertex_connected_n_chunk() = 0;
14849
14850 } // if (connect_to_the_right_flag != -1)
14851
14852 } // if (iproc == my_rank || jproc == my_rank)
14853 }
14854
14855 //======================================================================
14856 /// Reset the boundary elements info. after load balance have
14857 /// taken place
14858 //======================================================================
14859 template<class ELEMENT>
14861 Vector<unsigned>& ntmp_boundary_elements,
14862 Vector<Vector<unsigned>>& ntmp_boundary_elements_in_region,
14863 Vector<FiniteElement*>& deleted_elements)
14864 {
14865 // Get the number of boundaries
14866 const unsigned nbound = this->nboundary();
14867
14868 // Are there regions?
14869 const unsigned n_regions = this->nregion();
14870
14871 // Loop over the boundaries
14872 for (unsigned b = 0; b < nbound; b++)
14873 {
14874 // Get the boundary elements and back them up
14875 // -----------------------------------------------------------------
14876 // Get the number of boundary elements (mixed with the old and new)
14877 const unsigned nbound_ele = this->nboundary_element(b);
14878 // Back-up the boundary elements
14879 Vector<FiniteElement*> backed_up_boundary_element_pt(nbound_ele);
14880 Vector<int> backed_up_face_index_at_boundary(nbound_ele);
14881 for (unsigned e = 0; e < nbound_ele; e++)
14882 {
14883 // Get the old boundary element
14884 backed_up_boundary_element_pt[e] = this->boundary_element_pt(b, e);
14885 // Get the old face index
14886 backed_up_face_index_at_boundary[e] =
14887 this->face_index_at_boundary(b, e);
14888 } // for (n < nold_boundary_elements)
14889
14890 // Back up the elements in boundary for each region
14891 Vector<Vector<FiniteElement*>> backed_up_boundary_region_element_pt(
14892 n_regions);
14893 Vector<Vector<int>> backed_up_face_index_at_boundary_region(n_regions);
14894
14895 // Loop over the regions and back up the boundary elements in
14896 // regions
14897 for (unsigned ir = 0; ir < n_regions; ir++)
14898 {
14899 // Get the region id
14900 const unsigned region_id =
14901 static_cast<unsigned>(this->region_attribute(ir));
14902 // Get the number of boundary region elements (mixed old and new)
14903 const unsigned nbnd_region_ele =
14904 this->nboundary_element_in_region(b, region_id);
14905
14906 // Loop over the elements in the region
14907 for (unsigned e = 0; e < nbnd_region_ele; e++)
14908 {
14909 // Get the old boundary region element
14910 backed_up_boundary_region_element_pt[ir][e] =
14911 this->boundary_element_in_region_pt(b, region_id, e);
14912
14913 // Get the old face index
14914 backed_up_face_index_at_boundary_region[ir][e] =
14915 this->face_index_at_boundary_in_region(b, region_id, e);
14916 } // for (e < nbnd_region_ele)
14917
14918 } // for (ir < n_regions)
14919
14920 // Clean all previous storages
14921 this->Boundary_element_pt[b].clear();
14922 this->Face_index_at_boundary[b].clear();
14923 if (n_regions > 0)
14924 {
14925 this->Boundary_region_element_pt[b].clear();
14926 this->Face_index_region_at_boundary[b].clear();
14927 }
14928
14929 // -------------------------------------------------------------------
14930 // Now copy only the elements that are still alive, from those before
14931 // the re-establishment of halo and haloed elements
14932 // -------------------------------------------------------------------
14933 // Start with the boundary elements
14934 // Get the old number of boundary elements
14935 const unsigned nold_bnd_ele = ntmp_boundary_elements[b];
14936 // Loop over the boundary elements and check those still alive
14937 for (unsigned e = 0; e < nold_bnd_ele; e++)
14938 {
14939 FiniteElement* tmp_ele_pt = backed_up_boundary_element_pt[e];
14940 // Include only those elements still alive
14941 Vector<FiniteElement*>::iterator it = std::find(
14942 deleted_elements.begin(), deleted_elements.end(), tmp_ele_pt);
14943 // Only copy thoes elements not found on the deleted elements
14944 // container
14945 if (it == deleted_elements.end())
14946 {
14947 FiniteElement* add_ele_pt = backed_up_boundary_element_pt[e];
14948 this->Boundary_element_pt[b].push_back(add_ele_pt);
14949 const int face_index = backed_up_face_index_at_boundary[e];
14950 this->Face_index_at_boundary[b].push_back(face_index);
14951 } // if (tmp_ele_pt != 0)
14952
14953 } // for (n < nold_bnd_ele)
14954
14955 // ... continue with the boundary elements in specific regions
14956
14957 // Loop over the regions
14958 for (unsigned ir = 0; ir < n_regions; ir++)
14959 {
14960 // Get the region id
14961 const unsigned region_id =
14962 static_cast<unsigned>(this->region_attribute(ir));
14963
14964 // Get the old number of boundary elements in region
14965 const unsigned nold_bnd_region_ele =
14966 ntmp_boundary_elements_in_region[b][ir];
14967
14968 // Loop over the boundary region elements and check those still
14969 // alive
14970 for (unsigned e = 0; e < nold_bnd_region_ele; e++)
14971 {
14972 // Get the element
14973 FiniteElement* tmp_ele_pt =
14974 backed_up_boundary_region_element_pt[ir][e];
14975 // Include only those elements still alive
14976 Vector<FiniteElement*>::iterator it = std::find(
14977 deleted_elements.begin(), deleted_elements.end(), tmp_ele_pt);
14978 // Only copy those elements not found on the deleted elements
14979 // container
14980 if (it == deleted_elements.end())
14981 {
14982 FiniteElement* add_ele_pt =
14983 backed_up_boundary_region_element_pt[ir][e];
14984 this->Boundary_region_element_pt[b][region_id].push_back(
14985 add_ele_pt);
14986 const int face_index =
14987 backed_up_face_index_at_boundary_region[ir][e];
14988 this->Face_index_region_at_boundary[b][region_id].push_back(
14989 face_index);
14990 } // if (tmp_ele_pt != 0)
14991
14992 } // for (n < nbound_ele)
14993
14994 } // for (ir < n_regions)
14995
14996 // ----------------------------------------------------------------
14997 // Now copy all those elements created after the re-establishment
14998 // of halo and haloed elements
14999 // ----------------------------------------------------------------
15000 // Loop over the boundary elements
15001 for (unsigned e = nold_bnd_ele; e < nbound_ele; e++)
15002 {
15003 FiniteElement* add_ele_pt = backed_up_boundary_element_pt[e];
15004 this->Boundary_element_pt[b].push_back(add_ele_pt);
15005 const int face_index = backed_up_face_index_at_boundary[e];
15006 this->Face_index_at_boundary[b].push_back(face_index);
15007 } // for (e < nbound_ele)
15008
15009 // Now add the boundary elements in regions
15010
15011 // Loop over the regions
15012 for (unsigned ir = 0; ir < n_regions; ir++)
15013 {
15014 // Get the region id
15015 const unsigned region_id =
15016 static_cast<unsigned>(this->region_attribute(ir));
15017
15018 // Get the old number of boundary elements in region
15019 const unsigned nold_bnd_region_ele =
15020 ntmp_boundary_elements_in_region[b][ir];
15021
15022 // Get the new number of boundary elements in region
15023 const unsigned nbnd_region_ele =
15024 this->nboundary_element_in_region(b, region_id);
15025
15026 // Loop over the boundary region elements and check those still
15027 // alive
15028 for (unsigned e = nold_bnd_region_ele; e < nbnd_region_ele; e++)
15029 {
15030 FiniteElement* add_ele_pt =
15031 backed_up_boundary_region_element_pt[ir][e];
15032 this->Boundary_region_element_pt[b][region_id].push_back(add_ele_pt);
15033 const int face_index = backed_up_face_index_at_boundary_region[ir][e];
15034 this->Face_index_region_at_boundary[b][region_id].push_back(
15035 face_index);
15036 } // for (e < nbnd_region_ele)
15037
15038 } // for (ir < n_regions)
15039
15040 } // for (b < nbound)
15041
15042 // Lookup scheme has now been setup yet
15043 Lookup_for_elements_next_boundary_is_setup = true;
15044 }
15045
15046#endif // OOMPH_HAS_MPI
15047
15048#ifdef OOMPH_HAS_TRIANGLE_LIB
15049
15050 //========================================================================
15051 /// Build a new TriangulateIO object based on target areas specified
15052 //========================================================================
15053 template<class ELEMENT>
15055 TriangulateIO& triangulate_io,
15056 const Vector<double>& target_area,
15057 struct TriangulateIO& triangle_refine)
15058 {
15059 // Initialize
15061
15062 // Store the global number of vertices and segments
15063 // in the list
15064 unsigned n_points = triangulate_io.numberofpoints;
15065 triangle_refine.numberofpoints = n_points;
15066
15067 unsigned n_segments = triangulate_io.numberofsegments;
15068 triangle_refine.numberofsegments = n_segments;
15069
15070 // Initialization of the TriangulateIO objects to store the values
15071 triangle_refine.pointlist =
15072 (double*)malloc(triangulate_io.numberofpoints * 2 * sizeof(double));
15073 triangle_refine.pointmarkerlist =
15074 (int*)malloc(triangulate_io.numberofpoints * sizeof(int));
15075 triangle_refine.segmentlist =
15076 (int*)malloc(triangulate_io.numberofsegments * 2 * sizeof(int));
15077 triangle_refine.segmentmarkerlist =
15078 (int*)malloc(triangulate_io.numberofsegments * sizeof(int));
15079
15080 // Storing the point's coordinates in the list
15081 // and in two vectors with x and y coordinates
15082 Vector<double> x_coord(n_points);
15083 Vector<double> y_coord(n_points);
15084
15085 for (unsigned count_point = 0; count_point < n_points * 2; count_point++)
15086 {
15087 triangle_refine.pointlist[count_point] =
15088 triangulate_io.pointlist[count_point];
15089
15090 // Even vaules represent the x coordinate
15091 // Odd values represent the y coordinate
15092 if (count_point % 2 == 0)
15093 {
15094 x_coord[count_point / 2] = triangulate_io.pointlist[count_point];
15095 }
15096 else
15097 {
15098 y_coord[(count_point - 1) / 2] = triangulate_io.pointlist[count_point];
15099 }
15100 }
15101
15102 // Store the point's markers in the list
15103 for (unsigned count_marker = 0; count_marker < n_points; count_marker++)
15104 {
15105 triangle_refine.pointmarkerlist[count_marker] =
15106 triangulate_io.pointmarkerlist[count_marker];
15107 }
15108
15109 // Storing the segment's edges in the list
15110 for (unsigned count_seg = 0; count_seg < n_segments * 2; count_seg++)
15111 {
15112 triangle_refine.segmentlist[count_seg] =
15113 triangulate_io.segmentlist[count_seg];
15114 }
15115
15116 // Store the segment's markers in the list
15117 for (unsigned count_markers = 0; count_markers < n_segments;
15118 count_markers++)
15119 {
15120 triangle_refine.segmentmarkerlist[count_markers] =
15121 triangulate_io.segmentmarkerlist[count_markers];
15122 }
15123
15124 // Store the hole's center coordinates
15125 unsigned n_holes = triangulate_io.numberofholes;
15126 triangle_refine.numberofholes = n_holes;
15127
15128 triangle_refine.holelist =
15129 (double*)malloc(triangulate_io.numberofholes * 2 * sizeof(double));
15130
15131 // Loop over the holes to get centre coords
15132 for (unsigned count_hole = 0; count_hole < n_holes * 2; count_hole++)
15133 {
15134 triangle_refine.holelist[count_hole] =
15135 triangulate_io.holelist[count_hole];
15136 }
15137
15138 // Store the triangles values
15139 unsigned n_triangles = triangulate_io.numberoftriangles;
15140 triangle_refine.numberoftriangles = n_triangles;
15141
15142#ifdef PARANOID
15143 if (n_triangles != target_area.size())
15144 {
15145 std::stringstream err;
15146 err << "Number of triangles in triangulate_io=" << n_triangles
15147 << " doesn't match\n"
15148 << "size of target area vector (" << target_area.size() << ")\n";
15149 throw OomphLibError(
15150 err.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
15151 }
15152#endif
15153
15154 unsigned n_corners = triangulate_io.numberofcorners;
15155 triangle_refine.numberofcorners = n_corners;
15156
15157 triangle_refine.trianglelist =
15158 (int*)malloc(triangulate_io.numberoftriangles * 3 * sizeof(int));
15159
15160 // Store the triangle's corners in the list and get element sizes
15161 for (unsigned count_tri = 0; count_tri < n_triangles * 3; count_tri++)
15162 {
15163 triangle_refine.trianglelist[count_tri] =
15164 triangulate_io.trianglelist[count_tri];
15165 }
15166
15167 // Store the triangle's area in the list
15168 triangle_refine.trianglearealist =
15169 (double*)malloc(triangulate_io.numberoftriangles * sizeof(double));
15170 for (unsigned count_area = 0; count_area < n_triangles; count_area++)
15171 {
15172 triangle_refine.trianglearealist[count_area] = target_area[count_area];
15173 }
15174
15175 // Store the triangles attributes in the list
15176 triangle_refine.numberoftriangleattributes =
15177 triangulate_io.numberoftriangleattributes;
15178
15179 triangle_refine.triangleattributelist = (double*)malloc(
15180 triangulate_io.numberoftriangles *
15181 triangulate_io.numberoftriangleattributes * sizeof(double));
15182 for (unsigned count_attribute = 0;
15183 count_attribute <
15184 (n_triangles * triangulate_io.numberoftriangleattributes);
15185 count_attribute++)
15186 {
15187 triangle_refine.triangleattributelist[count_attribute] =
15188 triangulate_io.triangleattributelist[count_attribute];
15189 }
15190 }
15191
15192#ifdef OOMPH_HAS_MPI
15193
15194 // ===================================================================
15195 // The comparison class for the map that sorts the nodes on the
15196 // shared boundary (using a lexicographic order)
15197 // ===================================================================
15199 {
15200 // Tolerance for lower-left comparison
15201 static double Tol;
15202
15203
15204 // Comparison operator for "lower left" ordering
15205 bool operator()(const std::pair<double, double>& lhs,
15206 const std::pair<double, double>& rhs) const
15207 {
15208 double diff_y = lhs.second - rhs.second;
15209 if (diff_y < -Tol) // (lhs.second < rhs.second)
15210 {
15211 return true;
15212 }
15213 else
15214 {
15215 // Are they "equal" with 1.0e-14 tolerance?
15216 if (diff_y < Tol) // (lhs.second == rhs.second)
15217 {
15218#ifdef PARANOID
15219 double diff_x = lhs.first - rhs.first;
15220 if (fabs(diff_x) < Tol)
15221 {
15222 std::ostringstream warning_message;
15223 warning_message
15224 << "Dodgy \"lower left\" (lexicographic) comparison "
15225 << "of points with cooordinates: "
15226 << " lhs = ( " << lhs.first << " , " << lhs.second << " ) \n"
15227 << " rhs = ( " << rhs.first << " , " << rhs.second << " ) \n"
15228 << "x and y coordinates differ by less than tolerance!\n"
15229 << "diff_x = " << diff_x << "\n"
15230 << "diff_y = " << diff_y << "\n"
15231 << "Tol = " << Tol << "\n";
15232 OomphLibError(warning_message.str(),
15233 OOMPH_CURRENT_FUNCTION,
15234 OOMPH_EXCEPTION_LOCATION);
15235 }
15236#endif
15237 if (lhs.first < rhs.first)
15238 {
15239 return true;
15240 }
15241 else
15242 {
15243 return false;
15244 }
15245 }
15246 else
15247 {
15248 return false;
15249 }
15250 }
15251
15252
15253 // if (lhs.second < rhs.second)
15254 // {
15255 // return true;
15256 // }
15257 // else
15258 // {
15259 // // // Are "equal" with 1.0e-14 tolerance
15260 // // if (lhs.second - rhs.second < 1.0e-14)
15261 // // Are equal?
15262 // if (lhs.second == rhs.second)
15263 // {
15264 // if (lhs.first < rhs.first)
15265 // {
15266 // return true;
15267 // }
15268 // else
15269 // {
15270 // return false;
15271 // }
15272 // }
15273 // else
15274 // {
15275 // return false;
15276 // }
15277 // }
15278 }
15279
15280 } Bottom_left_sorter; // struct classcomp
15281
15282
15283 // Assign value for tolerance
15284 double classcomp::Tol = 1.0e-14;
15285
15286
15287 //======================================================================
15288 // Sort the nodes on shared boundaries so that the processors that share
15289 // a boundary agree with the order of the nodes on the boundary
15290 //======================================================================
15291 template<class ELEMENT>
15293 {
15294 // Get the shared boundaries in this processor
15295 Vector<unsigned> my_rank_shared_boundaries_ids;
15296 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
15297
15298 // Get the number of shared boundaries
15299 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
15300
15301 // Loop over the shared boundaries
15302 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
15303 {
15304 // A map is used to sort the nodes using their coordinates as the key
15305 // of the map
15306 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
15307 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
15308
15309
15310#ifdef PARANOID
15311
15312 // Check min distance between nodes; had better be less than the
15313 // tolerance used for the bottom left sorting
15314 double min_distance_squared = DBL_MAX;
15315
15316#endif
15317
15318 // Get the boundary id
15319 const unsigned b = my_rank_shared_boundaries_ids[i];
15320
15321 // Get the number of nodes on the current boundary
15322 const unsigned nbnd_node = this->nshared_boundary_node(b);
15323
15324 // Go through all the nodes on the boundary and temporarily store
15325 // them on the map container
15326 for (unsigned i_node = 0; i_node < nbnd_node; i_node++)
15327 {
15328 Node* node_pt = this->shared_boundary_node_pt(b, i_node);
15329 std::pair<double, double> vertex =
15330 std::make_pair(node_pt->x(0), node_pt->x(1));
15331 sorted_nodes_pt[vertex] = node_pt;
15332
15333
15334#ifdef PARANOID
15335
15336 // Check for minimum distance
15337 for (unsigned j_node = 0; j_node < nbnd_node; j_node++)
15338 {
15339 if (i_node != j_node)
15340 {
15341 Node* node2_pt = this->shared_boundary_node_pt(b, j_node);
15342
15343 // Squared distance
15344 double squared_distance = 0.0;
15345 for (unsigned ii = 0; ii < 2; ii++)
15346 {
15347 squared_distance += (node_pt->x(ii) - node2_pt->x(ii)) *
15348 (node_pt->x(ii) - node2_pt->x(ii));
15349 }
15350 if (squared_distance < min_distance_squared)
15351 {
15352 min_distance_squared = squared_distance;
15353 }
15354 }
15355 }
15356
15357 if (sqrt(min_distance_squared) < Bottom_left_sorter.Tol)
15358 {
15359 std::ostringstream warning_message;
15360 warning_message << "Minimum distance between nodes on boundary " << b
15361 << "\n"
15362 << "is " << sqrt(min_distance_squared)
15363 << " which is less than "
15364 << "Bottom_left_sorter.Tol = "
15365 << Bottom_left_sorter.Tol << "\n"
15366 << "This may screw up the ordering of the nodes on "
15367 "shared boundaries\n";
15368 OomphLibWarning(warning_message.str(),
15369 OOMPH_CURRENT_FUNCTION,
15370 OOMPH_EXCEPTION_LOCATION);
15371 }
15372
15373#endif
15374 }
15375
15376 unsigned counter = 0;
15377 // Resize the sorted shared boundary node vector
15378 this->Sorted_shared_boundary_node_pt[b].resize(nbnd_node);
15379
15380 // Now go through the map container, get the elements and store their
15381 // members on the Sorted_shared_boundary_node_pt container
15382 // The map has already sorted the nodes, now they keep the same sorting
15383 // on all processors
15384 for (std::map<std::pair<double, double>, Node*>::iterator it_map =
15385 sorted_nodes_pt.begin();
15386 it_map != sorted_nodes_pt.end();
15387 it_map++)
15388 {
15389 // Store the pointer to the node
15390 this->Sorted_shared_boundary_node_pt[b][counter++] = (*it_map).second;
15391 }
15392
15393 } // for (i < nmy_rank_shd_bnd)
15394 }
15395
15396 //========================================================================
15397 // Re-establish the shared boundary elements after the adaptation
15398 // process (the updating of shared nodes is optional and performed by
15399 // default)
15400 //========================================================================
15401 template<class ELEMENT>
15403 reset_shared_boundary_elements_and_nodes(const bool flush_elements,
15404 const bool update_elements,
15405 const bool flush_nodes,
15406 const bool update_nodes)
15407 {
15408 // Get the rank of the current processor
15409 const unsigned my_rank = this->communicator_pt()->my_rank();
15410
15411 // Go through the boundaries know as shared boundaries and copy the
15412 // elements to the corresponding storage
15413
15414 // Get the initial shared boundary id
15415 const unsigned initial_id = this->initial_shared_boundary_id();
15416
15417 // Get the final shared boundary id
15418 const unsigned final_id = this->final_shared_boundary_id();
15419
15420 if (flush_elements)
15421 {
15422 // Flush the shared boundaries storage for elements
15423 this->flush_shared_boundary_element();
15424 // .. and also flush the face indexes associated with the element
15425 this->flush_face_index_at_shared_boundary();
15426 } // if (flush_elements)
15427
15428 if (flush_nodes)
15429 {
15430 // Flush the shared boundaries storage for nodes
15431 this->flush_shared_boundary_node();
15432 } // if (flush_nodes)
15433
15434 for (unsigned b = initial_id; b < final_id; b++)
15435 {
15436 // Check if the boundary is on the current processor
15437 Vector<unsigned> procs_from_shrd_bnd;
15438 procs_from_shrd_bnd = this->shared_boundary_from_processors(b);
15439 bool current_processor_has_b_boundary = false;
15440 const unsigned n_procs_from_shrd_bnd = procs_from_shrd_bnd.size();
15441 for (unsigned p = 0; p < n_procs_from_shrd_bnd; p++)
15442 {
15443 if (procs_from_shrd_bnd[p] == my_rank)
15444 {
15445 current_processor_has_b_boundary = true;
15446 break; // break for (p < n_procs_from_shrd_bnd)
15447 }
15448 } // for (p < n_procs_from_shrd_bnd)
15449
15450 if (current_processor_has_b_boundary)
15451 {
15452 if (update_elements)
15453 {
15454 const unsigned nboundary_ele = this->nboundary_element(b);
15455 for (unsigned e = 0; e < nboundary_ele; e++)
15456 {
15457 // Get the boundary element and add it to the shared
15458 // boundary elements structure
15459 FiniteElement* bnd_ele_pt = this->boundary_element_pt(b, e);
15460 this->add_shared_boundary_element(b, bnd_ele_pt);
15461 // ... do the same with the face index information
15462 int face_index = this->face_index_at_boundary(b, e);
15463 this->add_face_index_at_shared_boundary(b, face_index);
15464 } // for (e < nboundary_element)
15465 } // if (update_elements)
15466
15467 if (update_nodes)
15468 {
15469 const unsigned nboundary_node = this->nboundary_node(b);
15470 for (unsigned n = 0; n < nboundary_node; n++)
15471 {
15472 Node* bnd_node_pt = this->boundary_node_pt(b, n);
15473 this->add_shared_boundary_node(b, bnd_node_pt);
15474 } // for (n < nboundary_node)
15475 } // if (update_nodes)
15476
15477 } // if (current_processor_has_b_boundary)
15478 } // for (b < final_id)
15479 }
15480
15481 //======================================================================
15482 // Sort the nodes on shared boundaries so that the processors that share
15483 // a boundary agree with the order of the nodes on the boundary
15484 //======================================================================
15485 template<class ELEMENT>
15487 {
15488 // Get the number of processors
15489 unsigned nproc = this->communicator_pt()->nproc();
15490 // Get the rank of the current processor
15491 unsigned my_rank = this->communicator_pt()->my_rank();
15492
15493 // Get some timings
15494 double tt_start = 0.0;
15495 double tt_end = 0.0;
15497 {
15498 tt_start = TimingHelpers::timer();
15499 }
15500
15501 // -------------------------------------------------------------------
15502 // BEGIN: Get the node names and the shared nodes
15503 // -------------------------------------------------------------------
15504
15505 // Container where to store the nodes on shared boundaries no
15506 // associated with the processor that receives the elements/nodes
15507 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
15509 other_proc_shd_bnd_node_pt(nproc);
15510 // Resize the container
15511 for (unsigned iproc = 0; iproc < nproc; iproc++)
15512 {
15513 // Resize the container
15514 other_proc_shd_bnd_node_pt[iproc].resize(nproc);
15515 for (unsigned jproc = 0; jproc < nproc; jproc++)
15516 {
15517 // Get the number of shared boundaries
15518 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
15519 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
15520 const unsigned nshared_bound = final_shd_bnd_id - initial_shd_bnd_id;
15521 other_proc_shd_bnd_node_pt[iproc][jproc].resize(nshared_bound);
15522 } // for (jproc < nproc)
15523
15524 } // for (iproc < nproc)
15525
15526 // Store the global node names
15527 // global_node_name[x][ ][ ] Global node number
15528 // global_node_name[ ][x][ ] Global node names
15529 // global_node_name[ ][ ][x] Global node info.
15530 Vector<Vector<Vector<unsigned>>> global_node_names;
15531
15532 // Creates a map between the node name and the index of the global
15533 // node so we can access all its node names
15534 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
15535
15536 // Store the global shared nodes pointers
15537 Vector<Node*> global_shared_node_pt;
15538
15539 // Get the time for computation of global nodes names and shared
15540 // nodes
15541 double t_start_global_node_names_and_shared_nodes = TimingHelpers::timer();
15542
15543 // Compute all the names of the nodes and fill in the
15544 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
15545 // on this processor (my_rank) by looking over all their names
15546 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
15547 global_node_names,
15548 node_name_to_global_index,
15549 global_shared_node_pt);
15550
15551 // Compute the number of elements before adding new ones
15552 const unsigned n_ele = this->nelement();
15553
15554 if (Print_timings_level_adaptation > 1)
15555 {
15556 // The total time for computation of global nodes names and
15557 // shared nodes
15558 double t_final_global_node_names_and_shared_nodes =
15559 TimingHelpers::timer() - t_start_global_node_names_and_shared_nodes;
15560 oomph_info << "CPU for computing global node names and shared nodes "
15561 << "[n_ele=" << n_ele
15562 << "]: " << t_final_global_node_names_and_shared_nodes
15563 << std::endl;
15564 }
15565
15566 // -------------------------------------------------------------------
15567 // END: Get the node names and the shared nodes
15568 // -------------------------------------------------------------------
15569
15570 // -------------------------------------------------------------------
15571 // BEGIN: Using the global node names each processor sends info. of
15572 // the nodes shared with other processors regarding whether they are
15573 // on an original boundary or not. This is required so that at the
15574 // re-generation of halo(ed) elements stage they have the updated
15575 // information
15576 // -------------------------------------------------------------------
15577
15578 // Get the time for sending info. of shared nodes on original
15579 // boundaries
15580 double t_start_send_info_shd_nodes_on_original_bnds =
15582
15583 // Send the boundary node info. of nodes on shared boundaries across
15584 // processors
15585 send_boundary_node_info_of_shared_nodes(
15586 global_node_names, node_name_to_global_index, global_shared_node_pt);
15587
15588 if (Print_timings_level_adaptation > 1)
15589 {
15590 // The total time for sending info. of shared nodes lying on
15591 // original boundaries
15593 << "CPU for sending info. of shared nodes on original boundaries: "
15594 << TimingHelpers::timer() - t_start_send_info_shd_nodes_on_original_bnds
15595 << std::endl;
15596 }
15597
15598 // -------------------------------------------------------------------
15599 // END: Using the global node names each processor sends info. of
15600 // the nodes shared with other processors regarding whether they are
15601 // on an original boundary or not. This is required so that at the
15602 // re-generation of halo(ed) elements stage they have the updated
15603 // information
15604 // -------------------------------------------------------------------
15605
15606 // -------------------------------------------------------------------
15607 // BEGIN: Identify the elements of the mesh that have nodes on the
15608 // shared boundaries
15609 // -------------------------------------------------------------------
15610
15611 // Store the elements that have a node on a shared boundary with
15612 // other processors
15613 // ele_with_node_on_shd_bnd_pt[x][ ][ ]: iproc
15614 // ele_with_node_on_shd_bnd_pt[ ][x][ ]: ishd boundary with iproc
15615 // ele_with_node_on_shd_bnd_pt[ ][ ][x]: element with node on shared
15616 // boundary with iproc
15617 Vector<Vector<Vector<FiniteElement*>>> ele_with_node_on_shd_bnd_pt(nproc);
15618 // Resize the container with the number of shared boundaries within
15619 // each processor
15620
15621 // loop over the processors
15622 for (unsigned iproc = 0; iproc < nproc; iproc++)
15623 {
15624 const unsigned n_shd_bnd_iproc = this->nshared_boundaries(my_rank, iproc);
15625 ele_with_node_on_shd_bnd_pt[iproc].resize(n_shd_bnd_iproc);
15626 } // for (iproc < nproc)
15627
15628 // Go through all the elements and check whether any of their nodes
15629 // lies on any of the shared boundaries
15630
15631 // loop over the elements
15632 for (unsigned e = 0; e < n_ele; e++)
15633 {
15634 // Get the element
15635 FiniteElement* ele_pt = this->finite_element_pt(e);
15636 // Get the number of nodes
15637 const unsigned n_nodes = ele_pt->nnode();
15638 // loop over the nodes and check whether any of them lies on a
15639 // shared boundary
15640 for (unsigned n = 0; n < n_nodes; n++)
15641 {
15642 // Get the node
15643 Node* node_pt = ele_pt->node_pt(n);
15644
15645 // Now check whether the current node lies on a shared boundary
15646 // within any other processor
15647
15648 // loop over the processors
15649 for (unsigned iproc = 0; iproc < nproc; iproc++)
15650 {
15651 // The number of boundaries shared with the current processor
15652 // (if iproc==my_rank then there are no shared boundaries
15653 // between them)
15654 const unsigned n_shd_bnd_iproc =
15655 this->nshared_boundaries(my_rank, iproc);
15656
15657 // There are no info. with myself
15658 if (iproc != my_rank && n_shd_bnd_iproc > 0)
15659 {
15660 // Get the boundaries ids of the shared boundaries with
15661 // iproc processor
15662 Vector<unsigned> shd_bnd_ids =
15663 this->shared_boundaries_ids(my_rank, iproc);
15664
15665 // Loop over shd bnds with processor "iproc"
15666 for (unsigned isb = 0; isb < n_shd_bnd_iproc; isb++)
15667 {
15668 const unsigned shd_bnd_id = shd_bnd_ids[isb];
15669 const unsigned n_ele_shd_bnd =
15670 this->nshared_boundary_element(shd_bnd_id);
15671
15672 // Check if the node is on this boundary only if there are
15673 // elements on it
15674 if (n_ele_shd_bnd > 0 &&
15675 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
15676 {
15677 // Add the element into those that have a
15678 // node on the current shared boundary
15679 ele_with_node_on_shd_bnd_pt[iproc][isb].push_back(ele_pt);
15680
15681 } // Are there elements on the boundary and the node lies
15682 // on this boundary
15683
15684 } // for (isb < n_shd_bnd_iproc)
15685
15686 } // if (iproc != my_rank && n_shd_bnd_iproc > 0)
15687
15688 } // for (iproc < nproc)
15689
15690 } // for (n < n_nodes)
15691
15692 } // for (e < n_ele)
15693
15694 // -------------------------------------------------------------------
15695 // END: Identify the elements of the mesh that have nodes on the
15696 // shared boundaries
15697 // -------------------------------------------------------------------
15698
15699 // -------------------------------------------------------------------
15700 // BEGIN: Create the halo(ed) elements. Loop over the processors and
15701 // the shared boundaries within each processor. Get the elements on
15702 // the shared boundaries, mark them as haloed in this processor and
15703 // as halo on the element that will receive the info.
15704 // -------------------------------------------------------------------
15705
15706 // ********************************************************************
15707 // General strategy:
15708 // 1) Go through all the elements on the shared boundaries, mark these
15709 // elements as haloed, same as their nodes.
15710 // 2) Package the info. of the nodes and the elements.
15711 // 3) Send and receive the info across processors
15712 // 4) Unpackage it and create halo elements and nodes as indicated by
15713 // the received info.
15714 // ********************************************************************
15715
15716 // Keep track of the currently created nodes within each
15717 // processor. We need to keep track of these nodes so they can be
15718 // referred at a second stage.
15719 Vector<Vector<Node*>> iproc_currently_created_nodes_pt(nproc);
15720
15721 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15722 double t_start_regenerate_halo_ed_elements_nodes_first_stage =
15724
15725 // Go through all processors
15726 for (unsigned iproc = 0; iproc < nproc; iproc++)
15727 {
15728 // Send and receive info. to/from other processors
15729 if (iproc != my_rank)
15730 {
15731 // Get the number of boundaries shared with the send proc (iproc)
15732 const unsigned nshared_boundaries_with_iproc =
15733 this->nshared_boundaries(my_rank, iproc);
15734
15735 if (nshared_boundaries_with_iproc > 0)
15736 {
15737 // ******************************************************************
15738 // Stage 1
15739 // ******************************************************************
15740 // Step (1) Mark the elements adjacent to the shared boundaries as
15741 // haloed, mark the nodes on these elements as haloed nodes
15742 // Step (2) Create packages of information indicating the generation
15743 // of halo elements and nodes
15744 // ******************************************************************
15745
15746 // Clean send and receive buffers
15747 Flat_packed_unsigneds.clear();
15748 Flat_packed_doubles.clear();
15749#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15751#endif
15752
15753 // Get the boundaries ids shared with "iproc"
15754 Vector<unsigned> bound_shared_with_iproc;
15755 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
15756
15757 // Loop over shared boundaries with processor "iproc"
15758 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15759 {
15760 const unsigned bnd_id = bound_shared_with_iproc[bs];
15761 // DEBP(bnd_id);
15762 const unsigned nel_bnd = this->nshared_boundary_element(bnd_id);
15763 // DEBP(nel_bnd);
15764
15765 // Container to store the elements marked as haloed
15766 Vector<FiniteElement*> haloed_element;
15767
15768 // All the elements adjacent to the boundary should be
15769 // marked as haloed elements
15770 if (nel_bnd > 0)
15771 {
15772 // Map to know which element have been already added
15773 std::map<FiniteElement*, bool> already_added;
15774
15775 // Loop over the elements adjacent to boundary "bnd_id"
15776 for (unsigned e = 0; e < nel_bnd; e++)
15777 {
15778 // Get pointer to the element adjacent to boundary bnd_id
15779 FiniteElement* ele_pt =
15780 this->shared_boundary_element_pt(bnd_id, e);
15781
15782 // Check if the element has been already added. Elemets
15783 // are repeated if they have two faces on the shared
15784 // boundary
15785 if (!already_added[ele_pt])
15786 {
15787 // Add the element to the container of haloed elements
15788 haloed_element.push_back(ele_pt);
15789 // Mark the element as already added
15790 already_added[ele_pt] = true;
15791 }
15792
15793 } // for (e < nel_bnd)
15794
15795 // In addition to the elements on the boundary we also
15796 // need to mark (as haloed) any element on the mesh with a
15797 // node on the shared boundary
15798
15799 // Get the number of elements with a node on the current
15800 // shared boundary
15801 const unsigned n_ele_with_node_on_shd_bnd =
15802 ele_with_node_on_shd_bnd_pt[iproc][bs].size();
15803 // loop and add the elements that have a node on the
15804 // current shared boundary with the current processor
15805 for (unsigned iele = 0; iele < n_ele_with_node_on_shd_bnd; iele++)
15806 {
15807 // Get the element
15808 FiniteElement* ele_pt =
15809 ele_with_node_on_shd_bnd_pt[iproc][bs][iele];
15810 // Check if it has not been already added
15811 if (!already_added[ele_pt])
15812 {
15813 // Add it!!
15814 haloed_element.push_back(ele_pt);
15815 // Mark it as done
15816 already_added[ele_pt] = true;
15817 } // if (!already_added[ele_pt])
15818
15819 } // for (iele < n_ele_with_node_on_shd_bnd)
15820
15821 } // if (nel_bnd > 0)
15822
15823 // Get the total number of haloed elements
15824 const unsigned nhaloed_ele = haloed_element.size();
15825 // DEBP(nhaloed_ele);
15826 // DEBP(my_rank);
15827 // DEBP(iproc);
15828 // The very first data of the flat packed is the number of haloed
15829 // elements, this will be the number of halo element to create on
15830 // the receiver processor
15831 Flat_packed_unsigneds.push_back(nhaloed_ele);
15832#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15833 std::stringstream junk;
15834 junk << "Number of haloed elements " << nhaloed_ele;
15835 Flat_packed_unsigneds_string.push_back(junk.str());
15836#endif
15837
15838 // Loop over the marked haloed elements
15839 for (unsigned e = 0; e < nhaloed_ele; e++)
15840 {
15841 // Get pointer to the marked haloed element
15842 FiniteElement* ele_pt = haloed_element[e];
15843 const unsigned nroot_haloed_ele =
15844 this->nroot_haloed_element(iproc);
15845
15846 // Check if the element has been already added to the
15847 // halo(ed) scheme
15848 GeneralisedElement* gen_ele_pt = ele_pt;
15849 const unsigned haloed_ele_index =
15850 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
15851
15852 // Was the element added or only returned the index of the
15853 // element
15854 if (nroot_haloed_ele == haloed_ele_index)
15855 {
15856 Flat_packed_unsigneds.push_back(1);
15857#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15859 "Haloed element needs to be constructed");
15860#endif
15861
15862 // Get additional info. related with the haloed element
15863 get_required_elemental_information_helper(iproc, ele_pt);
15864
15865 // Get the nodes on the element
15866 const unsigned nnodes = ele_pt->nnode();
15867 for (unsigned j = 0; j < nnodes; j++)
15868 {
15869 Node* node_pt = ele_pt->node_pt(j);
15870
15871 // Package the info. of the nodes
15872 // The destination processor goes in the arguments
15873 add_haloed_node_helper(iproc, node_pt);
15874
15875 } // for (j < nnodes)
15876 } // add the element and send its nodes
15877 else // The haloed element already exists
15878 {
15879 Flat_packed_unsigneds.push_back(0);
15880#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15882 "Haloed element already exists");
15883#endif
15884 Flat_packed_unsigneds.push_back(haloed_ele_index);
15885#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15887 "Index of existing haloed element");
15888#endif
15889 } // else (next_haloed_ele == external_haloed_ele_index)
15890 } // for (e < nel_bnd)
15891
15892 } // for (bs < nshared_boundaries_with_iproc)
15893
15894 // *******************************************************************
15895 // Stage (2)
15896 // *******************************************************************
15897 // Step (1) Send and receive the data to create halo elements and
15898 // nodes
15899 // *******************************************************************
15900 // The processor to which send the elements
15901 int send_proc = static_cast<int>(iproc);
15902 // The processor from which receive the elements
15903 int recv_proc = static_cast<int>(iproc);
15904 send_and_receive_elements_nodes_info(send_proc, recv_proc);
15905
15906 // *******************************************************************
15907 // Stage (3)
15908 // *******************************************************************
15909 // Step (1) Unpackage the info and create the halo elements and nodes
15910 // *******************************************************************
15911
15912 // Reset the counters
15915
15916 // Loop over shared boundaries with processor "iproc"
15917 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
15918 {
15919 // Get the number of halo element to be created
15920 const unsigned nhaloed_ele =
15922
15923#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
15926 << " Number of elements need to be constructed "
15928 << std::endl;
15929#endif
15930
15931 // Loop over boundaries shared with processor "urecv_proc"
15932 for (unsigned e = 0; e < nhaloed_ele; e++)
15933 {
15934 // Create halo element from received info. of "iproc"
15935 // processor on the current processor
15936 create_halo_element(iproc,
15937 iproc_currently_created_nodes_pt[iproc],
15938 other_proc_shd_bnd_node_pt,
15939 global_node_names,
15940 node_name_to_global_index,
15941 global_shared_node_pt);
15942
15943 } // for (e < nhaloed_ele)
15944
15945 } // for (bs < nshared_boundaries_with_iproc)
15946
15947 } // if (nshared_bound_recv_proc > 0)
15948
15949 } // if (iproc != my_rank)
15950
15951 } // for (iproc < nproc) (general loop to send and receive info.)
15952
15953 if (Print_timings_level_adaptation > 1)
15954 {
15955 // Get the time to re-generate halo(ed) elements/nodes (first stage)
15956 double t_final_regenerate_halo_ed_elements_nodes_first_stage =
15958 t_start_regenerate_halo_ed_elements_nodes_first_stage;
15959
15960 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15961 << "(first stage) [n_ele=" << n_ele << "]: "
15962 << t_final_regenerate_halo_ed_elements_nodes_first_stage
15963 << std::endl;
15964 }
15965
15966 // -------------------------------------------------------------------
15967 // END: Create the halo(ed) elements. Loop over the processors and
15968 // the shared boundaries within each processor. Get the elements on
15969 // the shared boundaries, mark them as haloed in this processor and
15970 // as halo on the element that will receive the info.
15971 // -------------------------------------------------------------------
15972
15973 // -------------------------------------------------------------------
15974 // BEGIN: Create any additional haloed element, those that dont lie
15975 // on a shared boundary but that shared a node with other processor
15976 // -------------------------------------------------------------------
15977
15978 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15979 double t_start_regenerate_halo_ed_elements_nodes_second_stage =
15981
15982 // Create any additional halo(ed) elements between processors that
15983 // have no shared boundaries but that have shared nodes
15984 reset_halo_haloed_scheme_helper(other_proc_shd_bnd_node_pt,
15985 iproc_currently_created_nodes_pt,
15986 global_node_names,
15987 node_name_to_global_index,
15988 global_shared_node_pt);
15989
15990 if (Print_timings_level_adaptation > 1)
15991 {
15992 // Get the time to re-generate halo(ed) elements/nodes (second stage)
15993 double t_final_regenerate_halo_ed_elements_nodes_second_stage =
15995 t_start_regenerate_halo_ed_elements_nodes_second_stage;
15996
15997 oomph_info << "CPU for re-generating halo(ed) elements/nodes "
15998 << "(second stage) [n_ele=" << n_ele << "]: "
15999 << t_final_regenerate_halo_ed_elements_nodes_second_stage
16000 << std::endl;
16001 }
16002
16003 // -------------------------------------------------------------------
16004 // END: Create any additional haloed element, those that dont lie on
16005 // a shared boundary but that shared a node with other processor
16006 // -------------------------------------------------------------------
16007
16008 // Clean send and receive buffers
16009 Flat_packed_unsigneds.clear();
16010 Flat_packed_doubles.clear();
16011#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
16013#endif
16014
16015 // Document the timings for reseting halo and haloed scheme (without
16016 // classification of halo and haloed nodes)
16017 if (Print_timings_level_adaptation > 1)
16018 {
16019 tt_end = TimingHelpers::timer();
16020 oomph_info << "CPU for resetting halo-haloed scheme (without "
16021 "classification of halo and haloed nodes): "
16022 << tt_end - tt_start << std::endl;
16023 }
16024
16025 // ------------------------------------------------------------------
16026 // BEGIN: Classify halo(ed) elements and nodes
16027 // ------------------------------------------------------------------
16028 const bool report_stats = true;
16029 DocInfo tmp_doc_info;
16030 tmp_doc_info.disable_doc();
16031
16032 // Classify nodes
16033 this->classify_halo_and_haloed_nodes(tmp_doc_info, report_stats);
16034
16035 // Document the timings for reseting halo and haloed scheme (with
16036 // classification of halo and haloed nodes)
16037 if (Print_timings_level_adaptation > 1)
16038 {
16039 tt_end = TimingHelpers::timer();
16040 oomph_info << "CPU for resetting halo-haloed scheme (with classification "
16041 "of halo and haloed nodes): "
16042 << tt_end - tt_start << std::endl;
16043 }
16044
16045 // ------------------------------------------------------------------
16046 // END: Classify halo(ed) elements and nodes
16047 // ------------------------------------------------------------------
16048 }
16049
16050 //======================================================================
16051 // Compute the alias of the nodes on shared boundaries in this
16052 // (my_rank) processor with other processors. Also compute the alias
16053 // of nodes on shared boundaries of other processors with other
16054 // processors (useful when there is an element that requires to be
16055 // sent to this (my_rank) processor because there is a shared node
16056 // between this (my_rank) and other processors BUT there is not a
16057 // shared boundary between this and the other processor
16058 // ======================================================================
16059 template<class ELEMENT>
16062 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
16063 other_proc_shd_bnd_node_pt,
16064 Vector<Vector<Vector<unsigned>>>& global_node_names,
16065 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
16066 Vector<Node*>& global_shared_node_pt)
16067 {
16068 // Get the number of processors
16069 const unsigned nproc = this->communicator_pt()->nproc();
16070 // Get the rank of the current processor
16071 const unsigned my_rank = this->communicator_pt()->my_rank();
16072 // Get the communicator of the mesh
16073 OomphCommunicator* comm_pt = this->communicator_pt();
16074
16075 // ---------------------------------------------------------------
16076 // BEGIN: Get the elements adjacent to shared boundaries and give
16077 // a unique node number to the nodes on the shared boundaries in
16078 // this processor
16079 // ---------------------------------------------------------------
16080
16081 // Counter for the nodes on shared boundaries in this (my_rank)
16082 // processor
16083 unsigned counter_nodes = 0;
16084 // Keep track of visited nodes
16085 std::map<Node*, bool> done_node;
16086 // ... and its local node number
16087 std::map<Node*, unsigned> local_node_number;
16088 // ... and the inverted relation from local node number to node_pt
16089 Vector<Node*> local_node_pt;
16090
16091 // Stores the j-th node name associated with the i-th local node
16092 // on shared boundaries in this processor (my_rank)
16093 // local_node_names[i][j][0] = my_rank (this processor)
16094 // local_node_names[i][j][1] = iproc (the processor with which there
16095 // is a shared boundary)
16096 // local_node_names[i][j][2] = the shared boundary id between this
16097 // (my_rank) processor and iproc
16098 // processor
16099 // local_node_names[i][j][3] = the node index on the shared boundary
16100 // local_node_names[i][j][4] = the local node index (i). This may
16101 // be unnecessary since we alread know the
16102 // index but we also send this info. to
16103 // the root processor that is why we store
16104 // them here
16105 Vector<Vector<Vector<unsigned>>> local_node_names;
16106
16107 // loop over the processors
16108 for (unsigned iproc = 0; iproc < nproc; iproc++)
16109 {
16110 // There are not shared boundaries with myself
16111 if (iproc != my_rank)
16112 {
16113 // Get the number of shared boundaries with iproc
16114 const unsigned n_shd_bnds_with_iproc =
16115 this->nshared_boundaries(my_rank, iproc);
16116
16117 // Get the boundaries ids shared with iproc
16118 Vector<unsigned> bnd_shd_with_iproc =
16119 this->shared_boundaries_ids(my_rank, iproc);
16120
16121 // Loop over the shared boundaries with processor iproc
16122 for (unsigned ishd = 0; ishd < n_shd_bnds_with_iproc; ishd++)
16123 {
16124 // Keep track of visited nodes with this shared boundary
16125 std::map<Node*, bool> done_node_shd_bnd;
16126 // The boundary id
16127 unsigned shd_bnd_id = bnd_shd_with_iproc[ishd];
16128 // Get the number of element on the shared boundary
16129 const unsigned n_shd_bnd_ele =
16130 this->nshared_boundary_element(shd_bnd_id);
16131
16132 // loop over the elements adjacent to the shared boundary
16133 for (unsigned e = 0; e < n_shd_bnd_ele; e++)
16134 {
16135 // Get the element
16136 FiniteElement* ele_pt =
16137 this->shared_boundary_element_pt(shd_bnd_id, e);
16138
16139 // Get the number of nodes on the element
16140 const unsigned n_nodes = ele_pt->nnode();
16141
16142 // loop over the nodes of the current element
16143 for (unsigned n = 0; n < n_nodes; n++)
16144 {
16145 // Get the node
16146 Node* node_pt = ele_pt->node_pt(n);
16147
16148 // Has the node been visited with this shared boundary?
16149 // And, is this a node on the shd_bnd_id shared boundary
16150 // with processor iproc?
16151 if (!done_node_shd_bnd[node_pt] &&
16152 this->is_node_on_shared_boundary(shd_bnd_id, node_pt))
16153 {
16154 // Mark the done as done with this shared boundary
16155 done_node_shd_bnd[node_pt] = true;
16156
16157 // Get the index of the node on the shared boundary
16158 // -------------------------------------------------
16159 // Get the number of nodes on the shared boundary
16160 const unsigned n_nodes_shd_bnd =
16161 nsorted_shared_boundary_node(shd_bnd_id);
16162
16163 // The index
16164 unsigned index = 0;
16165
16166#ifdef PARANOID
16167 // Flag to know if the node has been found
16168 bool found_node_on_shared_boundary = false;
16169#endif
16170 // Loop over the nodes on the shared boundary to find
16171 // the node
16172 for (unsigned k = 0; k < n_nodes_shd_bnd; k++)
16173 {
16174 // Get the k-th node on the shared boundary
16175 Node* shd_bnd_node_pt =
16176 sorted_shared_boundary_node_pt(shd_bnd_id, k);
16177
16178 // Is the same node?
16179 if (shd_bnd_node_pt == node_pt)
16180 {
16181 // This is the index
16182 index = k;
16183#ifdef PARANOID
16184 // Mark as found
16185 found_node_on_shared_boundary = true;
16186#endif
16187 break; // break
16188
16189 } // if (shd_bnd_node_pt == node_pt)
16190
16191 } // for (k < n_nodes_shd_bnd)
16192
16193#ifdef PARANOID
16194 if (!found_node_on_shared_boundary)
16195 {
16196 std::ostringstream error_message;
16197 error_message << "The index of the node on boundary ("
16198 << shd_bnd_id << ") was not found.\n"
16199 << "These are the node coordinates\n"
16200 << "(" << node_pt->x(0) << "," << node_pt->x(1)
16201 << ").\n";
16202 throw OomphLibError(error_message.str(),
16203 OOMPH_CURRENT_FUNCTION,
16204 OOMPH_EXCEPTION_LOCATION);
16205 }
16206#endif
16207
16208 // Create the node name
16209 Vector<unsigned> node_name(5);
16210 node_name[0] = my_rank;
16211 node_name[1] = iproc;
16212 node_name[2] = shd_bnd_id;
16213 node_name[3] = index;
16214 // The node number is filled in the following if/else
16215 // node_name[4] = ?;
16216
16217 // Has the node already been visited?
16218 if (!done_node[node_pt])
16219 {
16220 // If not ...
16221
16222 // Add the node to the local nodes
16223 local_node_pt.push_back(node_pt);
16224
16225 // Assign a local node number to the node
16226 local_node_number[node_pt] = counter_nodes;
16227 // Store the local node number
16228 node_name[4] = counter_nodes;
16229 // Increase the counter of nodes
16230 counter_nodes++;
16231 // ... and mark it as visited
16232 done_node[node_pt] = true;
16233
16234 // Push back the node name (the first
16235 // one found for this node)
16236 Vector<Vector<unsigned>> first_node_name(1);
16237 first_node_name[0] = node_name;
16238 local_node_names.push_back(first_node_name);
16239 }
16240 else
16241 {
16242 // If yes ...
16243
16244 // Get the local node number
16245 unsigned node_number = local_node_number[node_pt];
16246
16247 // Store the local node number
16248 node_name[4] = node_number;
16249
16250 // Push back the node name for the
16251 // node number
16252 local_node_names[node_number].push_back(node_name);
16253 }
16254
16255 } // Is on shared boundary?
16256
16257 } // for (n < nnodes)
16258
16259 } // for (e < n_shd_bnd_ele)
16260
16261 } // for (ishd < n_shd_bnds_with_iproc)
16262
16263 } // if (iproc != my_rank)
16264
16265 } // for (iproc < nproc)
16266
16267 // ---------------------------------------------------------------
16268 // END: Get the elements adjacent to shared boundaries and give
16269 // a unique node number to the nodes on the shared boundaries in
16270 // this processor
16271 // ---------------------------------------------------------------
16272
16273 // ---------------------------------------------------------------
16274 // BEGIN: Package the names of the local nodes
16275 // ---------------------------------------------------------------
16276 // Counter for the number of names of the nodes
16277 unsigned n_total_local_names = 0;
16278 // Get the number of local nodes
16279 const unsigned n_local_nodes = local_node_names.size();
16280 // loop over the number of local nodes and get the number of names
16281 // of each node
16282 for (unsigned i = 0; i < n_local_nodes; i++)
16283 {
16284 // Get the number of names of the i-th local node
16285 const unsigned n_inode_names = local_node_names[i].size();
16286 // ... and add them to the total number of local names
16287 n_total_local_names += n_inode_names;
16288 } // for (i < n_local_nodes)
16289
16290 // We store five data per node name (my_rank,iproc,shd_bnd_id,idx,node#)
16291 // where node# is the node number on this processor (my_rank)
16292 const unsigned n_info_per_node_name = 5;
16293 // Storage for the flat package
16294 Vector<unsigned> flat_packed_send_udata(n_total_local_names *
16295 n_info_per_node_name);
16296 // A counter
16297 unsigned counter = 0;
16298 // loop over the local nodes
16299 for (unsigned i = 0; i < n_local_nodes; i++)
16300 {
16301 // Get the number of names of the i-th local node
16302 const unsigned n_inode_names = local_node_names[i].size();
16303 // loop over the names of the i-th local node
16304 for (unsigned j = 0; j < n_inode_names; j++)
16305 {
16306 // Store this processor id (my_rank)
16307 flat_packed_send_udata[counter++] = local_node_names[i][j][0];
16308 // Store the processor with which the shared boundary exist
16309 flat_packed_send_udata[counter++] = local_node_names[i][j][1];
16310 // Store the shared boundary id
16311 flat_packed_send_udata[counter++] = local_node_names[i][j][2];
16312 // Store the index of the node on the shared boundary
16313 flat_packed_send_udata[counter++] = local_node_names[i][j][3];
16314 // Store the local node number on this processor (my_rank)
16315 flat_packed_send_udata[counter++] = local_node_names[i][j][4];
16316 } // for (j < n_inode_names)
16317
16318 } // for (i < n_local_nodes)
16319
16320 // Reset the counter
16321 counter = 0;
16322
16323 // The number of data that will be sent to root from this
16324 // (my_rank) processor
16325 const unsigned n_udata_send_to_root = flat_packed_send_udata.size();
16326
16327 // ---------------------------------------------------------------
16328 // END: Package the names of the local nodes
16329 // ---------------------------------------------------------------
16330 // ---------------------------------------------------------------
16331 // BEGIN: Send the data to the root processor
16332 // ---------------------------------------------------------------
16333
16334 // The root processor is in charge of computing all the node names
16335 // of the nodes on the shared boundaries
16336
16337 // Choose the root processor
16338 const unsigned root_processor = 0;
16339
16340 // The vector where the root processor receives how many names
16341 // will receive from the other processors
16342 Vector<unsigned> root_n_names_per_processor(nproc);
16343
16344 // Send the number of names that the root processor will receive
16345 // from each processor
16346 MPI_Gather(&n_total_local_names,
16347 1,
16348 MPI_UNSIGNED,
16349 &root_n_names_per_processor[0],
16350 1,
16351 MPI_UNSIGNED,
16352 root_processor,
16353 comm_pt->mpi_comm());
16354
16355 // Get the total number of data to receive from all processor in
16356 // root
16357 unsigned root_n_total_udata_receive = 0;
16358 Vector<int> root_n_udata_to_receive(nproc, 0);
16359 for (unsigned iproc = 0; iproc < nproc; iproc++)
16360 {
16361 root_n_udata_to_receive[iproc] =
16362 root_n_names_per_processor[iproc] * n_info_per_node_name;
16363 root_n_total_udata_receive += root_n_udata_to_receive[iproc];
16364 }
16365
16366 // Stores and compute the offsets (in root) for the data received
16367 // from each processor
16368 Vector<int> root_uoffsets_receive(nproc, 0);
16369 root_uoffsets_receive[0] = 0;
16370 for (unsigned iproc = 1; iproc < nproc; iproc++)
16371 {
16372 // Compute the offset to obtain the data from each processor
16373 root_uoffsets_receive[iproc] =
16374 root_uoffsets_receive[iproc - 1] + root_n_udata_to_receive[iproc - 1];
16375 }
16376
16377 // Create at least one entry so we don't get a seg fault below
16378 if (flat_packed_send_udata.size() == 0)
16379 {
16380 flat_packed_send_udata.resize(1);
16381 }
16382
16383 // Vector where to receive the info on root from all processors
16384 Vector<unsigned> root_flat_packed_receive_udata(root_n_total_udata_receive);
16385 // Only root receive data, the others dont, then resize the
16386 // container to have at least one entry
16387 if (my_rank != root_processor)
16388 {
16389 // Create at least one entry so we don't get a seg fault below
16390 if (root_flat_packed_receive_udata.size() == 0)
16391 {
16392 root_flat_packed_receive_udata.resize(1);
16393 }
16394 } // if (my_rank!=root_processor)
16395
16396 // Send the info. to the root processor
16397 MPI_Gatherv(&flat_packed_send_udata[0], // Flat package to send
16398 // info. from each
16399 // processor
16400 n_udata_send_to_root, // Total number of data send
16401 // from each processor to root
16402 MPI_UNSIGNED,
16403 &root_flat_packed_receive_udata[0], // Container where
16404 // to receive the
16405 // info. from all
16406 // processors
16407 &root_n_udata_to_receive[0], // Number of data to
16408 // receive from each
16409 // processor
16410 &root_uoffsets_receive[0], // The offset to store the
16411 // info. from each
16412 // processor
16413 MPI_UNSIGNED,
16414 root_processor, // The processor that receives all the
16415 // info.
16416 comm_pt->mpi_comm());
16417
16418 // Clear and resize the flat package to send
16419 flat_packed_send_udata.clear();
16420 flat_packed_send_udata.resize(0);
16421 // ---------------------------------------------------------------
16422 // END: Send the data to the root processor
16423 // ---------------------------------------------------------------
16424
16425 // Container where root stores the info. that will be sent to all
16426 // processors. This includes the number of global nodes, the
16427 // number of names for each global node and the names
16428 Vector<unsigned> flat_packed_root_send_receive_udata;
16429
16430 // ---------------------------------------------------------------
16431 // BEGIN: Unpackage the info. received on root. Compute the alias
16432 // of the nodes
16433 // ---------------------------------------------------------------
16434 if (my_rank == root_processor)
16435 {
16436 // Compute all the names of a node
16437 // root_global_node_name[x][ ][ ] Global node number
16438 // root_global_node_name[ ][x][ ] Global node names
16439 // root_global_node_name[ ][ ][x] Global node info.
16440 Vector<Vector<Vector<unsigned>>> root_global_node_names;
16441
16442 // Store the info. extracted from the flat package sent to
16443 // root
16444 // root_local_node_names[x][ ] Node name
16445 // root_local_node_names[ ][x] Node info
16446 Vector<Vector<unsigned>> root_local_node_names;
16447
16448 // Extract all the node names
16449 unsigned rcounter = 0;
16450 // loop over the processors
16451 for (unsigned iproc = 0; iproc < nproc; iproc++)
16452 {
16453 // Get the number of node names received from iproc
16454 const unsigned n_local_names_iproc = root_n_names_per_processor[iproc];
16455 for (unsigned i = 0; i < n_local_names_iproc; i++)
16456 {
16457 // Get the i-thnode name from iproc
16458 Vector<unsigned> node_name(n_info_per_node_name);
16459 for (unsigned j = 0; j < n_info_per_node_name; j++)
16460 {
16461 node_name[j] = root_flat_packed_receive_udata[rcounter++];
16462 }
16463
16464 // Add the i-th node name
16465 root_local_node_names.push_back(node_name);
16466
16467 } // for (i < n_local_names_iproc)
16468
16469 } // for (iproc < nproc)
16470
16471 // Get the number of node names received
16472 const unsigned n_root_local_node_names = root_local_node_names.size();
16473
16474 // For each name of the node identify the position of its
16475 // counter-part
16476
16477 // Given a node name on the iproc,
16478 // (iproc, jproc, ishd_bnd, idx, local_node_number1)
16479 // its counter part must live in jproc, so we look for the
16480 // node name
16481 // (jproc, iproc, ishd_bnd, idx, local_node_number2)
16482
16483 // Store the index of the node name counter-part
16484 Vector<unsigned> node_name_counter_part(n_root_local_node_names);
16485
16486 // Keep track of the names of nodes already done
16487 std::map<Vector<unsigned>, bool> done_name;
16488
16489 // loop over the names of the nodes received from all
16490 // processors
16491 for (unsigned i = 0; i < n_root_local_node_names; i++)
16492 {
16493 // Get the i-th node name
16494 Vector<unsigned> node_name = root_local_node_names[i];
16495
16496 // Check if this name node has been already done
16497 if (!done_name[node_name])
16498 {
16499 // Mark it as done
16500 done_name[node_name] = true;
16501#ifdef PARANOID
16502 // Flag to indicate the counter-part name node was
16503 // found
16504 bool found_both_names_node = false;
16505#endif
16506 // Find the counter-part name node (start from j+1
16507 // since all previous have been found, otherwise we
16508 // would not be here)
16509 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16510 {
16511 Vector<unsigned> node_name_r = root_local_node_names[j];
16512
16513 // Check if this name node has been already done
16514 if (!done_name[node_name_r])
16515 {
16516 // Check whether this node is the
16517 // counter-part of the current name node
16518 if (node_name[0] == node_name_r[1] &&
16519 node_name[1] == node_name_r[0] &&
16520 node_name[2] == node_name_r[2] &&
16521 node_name[3] == node_name_r[3])
16522 {
16523 // Mark the name as node
16524 done_name[node_name_r] = true;
16525 // Store the index of the counter-part of
16526 // the current node name
16527 node_name_counter_part[i] = j;
16528 // ... and indicate the current node name
16529 // as the index of the counter-part
16530 node_name_counter_part[j] = i;
16531#ifdef PARANOID
16532 // The node has been found
16533 found_both_names_node = true;
16534#endif
16535 // Break the loop to find the
16536 // counter-part
16537 break;
16538 }
16539
16540 } // if (!done_name[node_name_r])
16541
16542 } // for (j < n_root_local_node_names)
16543#ifdef PARANOID
16544 // Check whether the node counter-part was found
16545 if (!found_both_names_node)
16546 {
16547 std::ostringstream error_message;
16548 error_message << "The counter-part of the current name node was "
16549 << "not found,\nthe current node name is:\n"
16550 << "iproc:(" << node_name[0] << ")\n"
16551 << "jproc:(" << node_name[1] << ")\n"
16552 << "ishd_bnd:(" << node_name[2] << ")\n"
16553 << "index:(" << node_name[3] << ")\n";
16554 throw OomphLibError(error_message.str(),
16555 OOMPH_CURRENT_FUNCTION,
16556 OOMPH_EXCEPTION_LOCATION);
16557 } // if (!found_both_names_node)
16558#endif
16559
16560 } // if (!done_name[node_name])
16561
16562 } // for (i < n_root_local_node_names)
16563
16564 // -----------------------------------------------------------
16565 // Look for all the names of each node received and store them
16566 // in the "global node names" container
16567
16568 // Keep track of the names of nodes already done
16569 done_name.clear();
16570 // loop over the names of the nodes received from all
16571 // processors
16572 for (unsigned i = 0; i < n_root_local_node_names; i++)
16573 {
16574 // Get the i-th node name
16575 Vector<unsigned> node_name = root_local_node_names[i];
16576
16577 // Check if this name node has been already done
16578 if (!done_name[node_name])
16579 {
16580 // Store all the names of the current node
16581 Vector<Vector<unsigned>> all_node_names;
16582
16583 // Add the name of the node as the initial node name
16584 all_node_names.push_back(node_name);
16585
16586 // Get the index of the counter-part
16587 unsigned idx_c = node_name_counter_part[i];
16588 // Get the counter-part of the node name
16589 Vector<unsigned> node_name_r = root_local_node_names[idx_c];
16590
16591 // Add the name of the counter-part of the node
16592 all_node_names.push_back(node_name_r);
16593 // We do not mark it as done since we are interested in
16594 // the names that the counter-part may generate
16595
16596 // Get the number of names for the current node (two at
16597 // the first time)
16598 unsigned n_current_names = all_node_names.size();
16599 // Counter to ensure to visit all the names of the current
16600 // node
16601 unsigned icounter = 0;
16602
16603 // Visit all the names of the current node
16604 while (icounter < n_current_names)
16605 {
16606 // Get the current node name
16607 Vector<unsigned> current_node_name = all_node_names[icounter];
16608
16609 // Search for other names for the current name of the
16610 // node, but first check if this has been already
16611 // visited
16612 if (!done_name[current_node_name])
16613 {
16614 // Mark it as done
16615 done_name[current_node_name] = true;
16616
16617 // loop over the names of the nodes (start from the
16618 // j+1 position, all previous node names have all
16619 // their names already assigned)
16620 for (unsigned j = i + 1; j < n_root_local_node_names; j++)
16621 {
16622 // Get the j-th node name
16623 Vector<unsigned> other_node_name = root_local_node_names[j];
16624
16625 // Is this name node already done
16626 if (!done_name[other_node_name])
16627 {
16628 // Is this another name for the current name node?
16629 if ((current_node_name[0] == other_node_name[0]) &&
16630 (current_node_name[4] == other_node_name[4]))
16631 {
16632 // Mark it as done. If we search again using the
16633 // "other_node_name" as the current node name we
16634 // are not going to find new nodes to add
16635 done_name[other_node_name] = true;
16636 // Before adding it check that it is not already
16637 // part of the names of the node
16638 Vector<Vector<unsigned>>::iterator it =
16639 std::find(all_node_names.begin(),
16640 all_node_names.end(),
16641 other_node_name);
16642 if (it == all_node_names.end())
16643 {
16644 all_node_names.push_back(other_node_name);
16645 // Get the index of the counter-part
16646 unsigned k = node_name_counter_part[j];
16647 // Get the counter-part of the node name
16648 Vector<unsigned> other_node_name_r =
16649 root_local_node_names[k];
16650 // Add the name of the counter-part of the
16651 // node only if it has not been previously
16652 // done
16653 if (!done_name[other_node_name_r])
16654 {
16655 all_node_names.push_back(other_node_name_r);
16656 }
16657 }
16658
16659 } // // Is this another name for the current name
16660 // node?
16661
16662 } // if (!done_name[other_node_name])
16663
16664 } // for (j < n_root_local_node_names)
16665
16666 } // if (!done_name[current_node_name])
16667
16668 // Get the number of names
16669 n_current_names = all_node_names.size();
16670 // Increase the icounter to indicate we have visited the
16671 // current name of the node
16672 icounter++;
16673
16674 } // while(icounter < n_current_names)
16675
16676 // We now have all the names for the i-th global node
16677 root_global_node_names.push_back(all_node_names);
16678
16679 } // if (!done_name[node_name])
16680
16681 } // for (i < n_root_local_node_names)
16682
16683 // -------------------------------------------------------------
16684 // Prepare the info to be sent to all processors. The number
16685 // of global nodes, the number of names for each global node,
16686 // and their respective names
16687 // -------------------------------------------------------------
16688
16689 // Clear the container
16690 flat_packed_root_send_receive_udata.clear();
16691 // Get the number of global nodes
16692 const unsigned n_global_nodes = root_global_node_names.size();
16693 // ... and store this info. to be sent from root to all
16694 // processors
16695 flat_packed_root_send_receive_udata.push_back(n_global_nodes);
16696
16697 // loop over the nodes
16698 for (unsigned i = 0; i < n_global_nodes; i++)
16699 {
16700 // Get the names of the i-th global node
16701 Vector<Vector<unsigned>> global_inode_names = root_global_node_names[i];
16702 // Get the number of names for the i-th global node
16703 const unsigned n_names_global_inode = global_inode_names.size();
16704 // ... and store this info. to be sent from root to all
16705 // processors
16706 flat_packed_root_send_receive_udata.push_back(n_names_global_inode);
16707 // loop over the names of the global i-th node
16708 for (unsigned j = 0; j < n_names_global_inode; j++)
16709 {
16710 // loop over the info. associated with each name
16711 for (unsigned k = 0; k < n_info_per_node_name; k++)
16712 {
16713 // Store the name info. of the current name in the
16714 // container to be sent from root to all processors
16715 flat_packed_root_send_receive_udata.push_back(
16716 global_inode_names[j][k]);
16717 } // for (k < n_info_per_node_name)
16718
16719 } // for (j < n_names_inode)
16720
16721 } // for (i < n_global_nodes)
16722
16723 } // if (my_rank == root_processor)
16724
16725 // ----------------------------------------------------------------
16726 // END: Unpackage the info. received on root. Compute the alias
16727 // of the nodes and prepare the info. to be sent back from
16728 // root to all processors
16729 // ----------------------------------------------------------------
16730
16731 // ---------------------------------------------------------------
16732 // BEGIN: Send the info. back to all processors, unpackage the
16733 // info. and create the map from node name to global node
16734 // index
16735 // ---------------------------------------------------------------
16736 // The number of data that root send to other processors.
16737 unsigned root_n_udata_sent_to_all_proc =
16738 flat_packed_root_send_receive_udata.size();
16739
16740 MPI_Bcast(&root_n_udata_sent_to_all_proc, // Data to send and
16741 // receive
16742 1,
16743 MPI_UNSIGNED,
16744 root_processor,
16745 comm_pt->mpi_comm());
16746
16747 // Resize the container if this is a processor that receives data
16748 if (my_rank != root_processor)
16749 {
16750 flat_packed_root_send_receive_udata.resize(root_n_udata_sent_to_all_proc);
16751 }
16752
16753 // Send the info. from root and receive it on all processors
16754 MPI_Bcast(&flat_packed_root_send_receive_udata[0], // Info. sent
16755 // from root to
16756 // all
16757 // processors
16758 root_n_udata_sent_to_all_proc, // Number of data sent
16759 // from root to each
16760 // procesor
16761 MPI_UNSIGNED,
16762 root_processor, // The processor that sends all the info.
16763 comm_pt->mpi_comm());
16764
16765 // Counter to extract the info.
16766 counter = 0;
16767 // Read the number of global nodes
16768 const unsigned n_global_nodes =
16769 flat_packed_root_send_receive_udata[counter++];
16770 // Store the global names of the nodes
16771 // global_node_name[x][ ][ ] Global node number
16772 // global_node_name[ ][x][ ] Global node names
16773 // global_node_name[ ][ ][x] Global node info.
16774 // Vector<Vector<Vector<unsigned> > > global_node_names(n_global_nodes);
16775 // Resize the input vector
16776 global_node_names.resize(n_global_nodes);
16777 // Now loop until all global nodes info. has been read
16778 unsigned n_read_global_nodes = 0;
16779 while (n_read_global_nodes < n_global_nodes)
16780 {
16781 // Read the number of names for the current global node
16782 const unsigned n_names_global_inode =
16783 flat_packed_root_send_receive_udata[counter++];
16784 // Counter for the global node
16785 const unsigned i = n_read_global_nodes;
16786 // Resize the container
16787 global_node_names[i].resize(n_names_global_inode);
16788 // loop over the names of the global inode
16789 for (unsigned j = 0; j < n_names_global_inode; j++)
16790 {
16791 // Resize the container
16792 global_node_names[i][j].resize(n_info_per_node_name);
16793 // loop over the info. of the j-th node name of the i-th
16794 // global node
16795 for (unsigned k = 0; k < n_info_per_node_name; k++)
16796 {
16797 // Read the k-th node info. from the j-th node name of
16798 // the i-th global node
16799 global_node_names[i][j][k] =
16800 flat_packed_root_send_receive_udata[counter++];
16801
16802 } // for (k < n_info_per_node_name)
16803
16804 // Create the map from the node name to the global node
16805 // index
16806 Vector<unsigned> node_name(n_info_per_node_name - 1);
16807 node_name[0] = global_node_names[i][j][0];
16808 node_name[1] = global_node_names[i][j][1];
16809 node_name[2] = global_node_names[i][j][2];
16810 node_name[3] = global_node_names[i][j][3];
16811 // Do not add the local index since it will not longer be
16812 // used. Additionally, we will not know the local node
16813 // index outside this method
16814 // node_name[4] = global_node_names[i][j][4];
16815 node_name_to_global_index[node_name] = i;
16816
16817 } // for (j < n_names_global_inode)
16818
16819 // Increase the counter for read global nodes
16820 n_read_global_nodes++;
16821
16822 } // while (n_read_global_nodes < n_global_nodes)
16823
16824#ifdef PARANOID
16825 // Check we have read all the info.
16826 if (counter != root_n_udata_sent_to_all_proc)
16827 {
16828 std::ostringstream error_stream;
16829 error_stream
16830 << "The info. received from root regarding the global names of "
16831 << "the nodes\nwas not completely read.\n"
16832 << "The number of data sent/received from root is: ("
16833 << root_n_udata_sent_to_all_proc << ")\n"
16834 << "The number of data read from the received info. is: (" << counter
16835 << ")\n\n";
16836 throw OomphLibError(
16837 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
16838 } // if (counter != root_n_udata_sent_to_all_proc)
16839#endif
16840
16841 // ---------------------------------------------------------------
16842 // END: Send the info. back to all processors, unpackage the info.
16843 // and create the map from node name to global node index
16844 // ---------------------------------------------------------------
16845
16846 // ---------------------------------------------------------------
16847 // BEGIN: Add the info. from the global node names into the
16848 // info. of the local node names. We do this because the
16849 // local node names have pointers to the nodes.
16850 // Additionally, create a map from the node name to the
16851 // index of its global node
16852 // ---------------------------------------------------------------
16853
16854 // Resize the global shared node pointers container
16855 global_shared_node_pt.resize(n_global_nodes, 0);
16856
16857 // loop over the number of global nodes
16858 for (unsigned i = 0; i < n_global_nodes; i++)
16859 {
16860 // Flag to indicate that the iglobal node is part of the nodes
16861 // on the current processor
16862 bool is_this_a_local_node_name = false;
16863 unsigned local_node_number;
16864 // Get the number of names of the i-th global node
16865 const unsigned n_names_global_inode = global_node_names[i].size();
16866 // loop over the names of the i-th global node
16867 for (unsigned j = 0; j < n_names_global_inode; j++)
16868 {
16869 // Get the node name info.
16870 const unsigned iproc = global_node_names[i][j][0];
16871 local_node_number = global_node_names[i][j][4];
16872
16873 // Check if this node name lives on this processor
16874 if (my_rank == iproc)
16875 {
16876 // The node is part of the local node names
16877 is_this_a_local_node_name = true;
16878 // Break
16879 break;
16880 } // if (my_rank == iproc)
16881
16882 } // for (j < n_names_global_inode)
16883
16884 // If the node is part of the local nodes then add the
16885 // additional names of the node in the local container
16886 if (is_this_a_local_node_name)
16887 {
16888#ifdef PARANOID
16889 // Check that the global node include at least all the names
16890 // of the node on this processor
16891 const unsigned n_names_local_node =
16892 local_node_names[local_node_number].size();
16893 unsigned n_names_found_on_global_name_node = 0;
16894#endif
16895
16896 // Add the pointer of the node into the global shared node
16897 // pointers container
16898 global_shared_node_pt[i] = local_node_pt[local_node_number];
16899
16900 // Add all the global names of the node onto the local node
16901 // names
16902
16903 // loop again over the names of the i-th global node
16904 for (unsigned j = 0; j < n_names_global_inode; j++)
16905 {
16906 // Get the node name info.
16907 const unsigned iproc = global_node_names[i][j][0];
16908
16909 // Is this a node name on this processor?
16910 if (iproc != my_rank)
16911 {
16912 // Add the name
16913 local_node_names[local_node_number].push_back(
16914 global_node_names[i][j]);
16915 }
16916#ifdef PARANOID
16917 else
16918 {
16919 const unsigned jproc = global_node_names[i][j][1];
16920 const unsigned ishd_bnd = global_node_names[i][j][2];
16921 const unsigned idx = global_node_names[i][j][3];
16922 const unsigned n_local_node = global_node_names[i][j][4];
16923 // loop over the names of the local node
16924 for (unsigned k = 0; k < n_names_local_node; k++)
16925 {
16926 if ((local_node_names[local_node_number][k][0] == iproc) &&
16927 (local_node_names[local_node_number][k][1] == jproc) &&
16928 (local_node_names[local_node_number][k][2] == ishd_bnd) &&
16929 (local_node_names[local_node_number][k][3] == idx) &&
16930 (local_node_names[local_node_number][k][4] == n_local_node))
16931 {
16932 // Increase the number of local nodes found on the
16933 // global nodes
16934 n_names_found_on_global_name_node++;
16935 } // found global node on local nodes
16936
16937 } // for (k < n_names_local_node)
16938
16939 } // if (iproc != my_rank)
16940#endif
16941
16942 } // for (j < n_names_global_inode)
16943
16944#ifdef PARANOID
16945 // The number of local nodes names must be the same as the the
16946 // number of global nodes names associated with this processor
16947 // (my_rank, that start with iproc = my_rank)
16948 if (n_names_local_node != n_names_found_on_global_name_node)
16949 {
16950 std::ostringstream error_stream;
16951 error_stream << "The local node names corresponding to the local "
16952 << "node (" << local_node_number << ") were\n"
16953 << "not found on the global node names.\n\n"
16954 << "These are the names of the local node\n"
16955 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16956 for (unsigned k = 0; k < n_names_local_node; k++)
16957 {
16958 error_stream << "Name(" << k
16959 << "): " << local_node_names[local_node_number][k][0]
16960 << ", " << local_node_names[local_node_number][k][1]
16961 << ", " << local_node_names[local_node_number][k][2]
16962 << ", " << local_node_names[local_node_number][k][3]
16963 << ", " << local_node_names[local_node_number][k][4]
16964 << "\n";
16965 }
16966
16967 error_stream << "\n\nThese are the names of the global node\n"
16968 << "Name k: iproc, jproc, ishd_bnd, idx. #node\n";
16969 for (unsigned k = 0; k < n_names_global_inode; k++)
16970 {
16971 error_stream << "Name(" << k << "): " << global_node_names[i][k][0]
16972 << ", " << global_node_names[i][k][1] << ", "
16973 << global_node_names[i][k][2] << ", "
16974 << global_node_names[i][k][3] << ", "
16975 << global_node_names[i][k][4] << "\n";
16976 }
16977
16978 throw OomphLibError(error_stream.str(),
16979 OOMPH_CURRENT_FUNCTION,
16980 OOMPH_EXCEPTION_LOCATION);
16981 }
16982#endif
16983
16984 } // if (is_this_a_local_node_name)
16985
16986 } // for (i < n_global_nodes)
16987
16988 // ---------------------------------------------------------------
16989 // END: Add the info. from the global node names into the info.
16990 // of the local node names. We do this because the local
16991 // node names have pointers to the nodes
16992 // ---------------------------------------------------------------
16993
16994 // ---------------------------------------------------------------
16995 // BEGIN: Fill the data structure other_proc_shd_bnd_node_pt with
16996 // the local nodes.
16997 // ---------------------------------------------------------------
16998
16999 // Loop over the local nodes and fill the
17000 // other_proc_shd_bnd_node_pt container with the corresponding
17001 // info. NOTE: We are using the old size of the local node names,
17002 // before adding the names of the global nodes so we only loop
17003 // over the local nodes and not global.
17004
17005 // Compute the local shared boudary id
17006 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
17007
17008 // loop over the local nodes names
17009 for (unsigned i = 0; i < n_local_nodes; i++)
17010 {
17011 // Get the number of names for the i-th local node
17012 const unsigned n_names = local_node_names[i].size();
17013 // Get a pointer to the first name of the node found on this
17014 // processor (this ensures that the node lives on this
17015 // processor)
17016 Node* node_pt = local_node_pt[i];
17017 // loop over the names of the i-th local node and add an entry
17018 // to the other_proc_shd_bnd_node_pt structure
17019 for (unsigned j = 0; j < n_names; j++)
17020 {
17021 // Get the node name info.
17022 const unsigned iproc = local_node_names[i][j][0];
17023 const unsigned jproc = local_node_names[i][j][1];
17024 const unsigned ishd_bnd =
17025 local_node_names[i][j][2] - initial_shd_bnd_id;
17026 const unsigned index = local_node_names[i][j][3];
17027 // We can ignore the last entry, it was just used to compute
17028 // the global node number by the root processor
17029
17030 // Get the smallest processor number
17031 if (iproc < jproc)
17032 {
17033 other_proc_shd_bnd_node_pt[iproc][jproc][ishd_bnd][index] = node_pt;
17034 }
17035 else
17036 {
17037 other_proc_shd_bnd_node_pt[jproc][iproc][ishd_bnd][index] = node_pt;
17038 }
17039
17040 } // for (j < n_names)
17041
17042 } // for (i < n_local_node_names)
17043
17044 // ---------------------------------------------------------------
17045 // END: Fill the data structure other_proc_shd_bnd_node_pt with
17046 // the local nodes.
17047 // ---------------------------------------------------------------
17048 }
17049
17050 //======================================================================
17051 // Get the original boundaries to which is associated each
17052 // shared node, and send the info. to the related processors. We
17053 // need to do this so that at the reset of halo(ed) info. stage,
17054 // the info. is updated
17055 template<class ELEMENT>
17057 Vector<Vector<Vector<unsigned>>>& global_node_names,
17058 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17059 Vector<Node*>& global_shared_node_pt)
17060 {
17061 // Get the rank and number of processors
17062 const unsigned nproc = this->communicator_pt()->nproc();
17063 const unsigned my_rank = this->communicator_pt()->my_rank();
17064
17065 // The number of nodes on shared boundaries
17066 const unsigned n_nodes_on_shd_bnds = global_node_names.size();
17067 // ---------------------------------------------------------
17068 // BEGIN: Get the shared nodes between each of processors
17069 // ---------------------------------------------------------
17070
17071 // Store the nodes on shared boundaries in this processor with other
17072 // processors
17073 Vector<std::set<Node*>> node_on_shd_bnd_pt(nproc);
17074
17075 // A map to get access to the global shared node number from the
17076 // node pointer
17077 std::map<Node*, unsigned> node_pt_to_global_shd_bnd_index;
17078
17079 // loop over the global nodes names and get only those in this
17080 // processor
17081 for (unsigned i = 0; i < n_nodes_on_shd_bnds; i++)
17082 {
17083 // Get the number of names of the current node on shared
17084 // boundaries
17085 const unsigned n_names = global_node_names[i].size();
17086 // loop over the names
17087 for (unsigned j = 0; j < n_names; j++)
17088 {
17089 // Store the node name
17090 Vector<unsigned> node_name(4);
17091 node_name[0] = global_node_names[i][j][0];
17092 node_name[1] = global_node_names[i][j][1];
17093 node_name[2] = global_node_names[i][j][2];
17094 node_name[3] = global_node_names[i][j][3];
17095
17096 // Check whether the node is in the current processor
17097 if (node_name[0] == my_rank)
17098 {
17099 // Check with which processor the node is shared
17100 const unsigned jproc = node_name[1];
17101
17102#ifdef PARANOID
17103 std::map<Vector<unsigned>, unsigned>::iterator it =
17104 node_name_to_global_index.find(node_name);
17105 if (it != node_name_to_global_index.end())
17106 {
17107 // Check whether the global node index correspond with that
17108 // of the current global node name
17109 if (i != (*it).second)
17110 {
17111 std::ostringstream error_message;
17112 error_message
17113 << "The global node number " << (*it).second
17114 << ") obtained from the current node\n"
17115 << "name is not the same as the current node number (" << i
17116 << ").\n\n"
17117 << "Node name:\n"
17118 << "iproc:" << node_name[0] << "\n"
17119 << "jproc:" << node_name[1] << "\n"
17120 << "shd_bnd_id:" << node_name[2] << "\n"
17121 << "index:" << node_name[3] << "\n\n";
17122 throw OomphLibError(error_message.str(),
17123 OOMPH_CURRENT_FUNCTION,
17124 OOMPH_EXCEPTION_LOCATION);
17125 }
17126 }
17127 else
17128 {
17129 std::ostringstream error_message;
17130 error_message
17131 << "The node name is not registerd as living in this processor.\n"
17132 << "Node name:\n"
17133 << "iproc:" << node_name[0] << "\n"
17134 << "jproc:" << node_name[1] << "\n"
17135 << "shd_bnd_id:" << node_name[2] << "\n"
17136 << "index:" << node_name[3] << "\n\n";
17137 throw OomphLibError(error_message.str(),
17138 OOMPH_CURRENT_FUNCTION,
17139 OOMPH_EXCEPTION_LOCATION);
17140 }
17141
17142#endif // #ifdef PARANOID
17143
17144 // Get the node pointer
17145 Node* node_pt = global_shared_node_pt[i];
17146
17147#ifdef PARANOID
17148 if (node_pt == 0)
17149 {
17150 std::ostringstream error_message;
17151 error_message << "There is not global shared node within this\n"
17152 << "global node number (" << i
17153 << "). The global shared\n"
17154 << "node pointer is null\n\n";
17155 throw OomphLibError(error_message.str(),
17156 OOMPH_CURRENT_FUNCTION,
17157 OOMPH_EXCEPTION_LOCATION);
17158 }
17159#endif // #ifdef PARANOID
17160
17161 // Add the node to the nodes on shared boundaries in this
17162 // processor
17163 node_on_shd_bnd_pt[jproc].insert(node_pt);
17164
17165 // And store the global node index
17166 node_pt_to_global_shd_bnd_index[node_pt] = i;
17167
17168 } // if (node_name[0]==my_rank)
17169 else if (node_name[1] == my_rank)
17170 {
17171 // Check with which processor the node is shared
17172 const unsigned jproc = node_name[0];
17173
17174#ifdef PARANOID
17175 std::map<Vector<unsigned>, unsigned>::iterator it =
17176 node_name_to_global_index.find(node_name);
17177 if (it != node_name_to_global_index.end())
17178 {
17179 // Check whether the global node index correspond with that
17180 // of the current global node name
17181 if (i != (*it).second)
17182 {
17183 std::ostringstream error_message;
17184 error_message
17185 << "The global node number " << (*it).second
17186 << ") obtained from the current node\n"
17187 << "name is not the same as the current node number (" << i
17188 << ").\n\n"
17189 << "Node name:\n"
17190 << "iproc:" << node_name[0] << "\n"
17191 << "jproc:" << node_name[1] << "\n"
17192 << "shd_bnd_id:" << node_name[2] << "\n"
17193 << "index:" << node_name[3] << "\n\n";
17194 throw OomphLibError(error_message.str(),
17195 OOMPH_CURRENT_FUNCTION,
17196 OOMPH_EXCEPTION_LOCATION);
17197 }
17198 }
17199 else
17200 {
17201 std::ostringstream error_message;
17202 error_message
17203 << "The node name is not registerd as living in this processor.\n"
17204 << "Node name:\n"
17205 << "iproc:" << node_name[0] << "\n"
17206 << "jproc:" << node_name[1] << "\n"
17207 << "shd_bnd_id:" << node_name[2] << "\n"
17208 << "index:" << node_name[3] << "\n\n";
17209 throw OomphLibError(error_message.str(),
17210 OOMPH_CURRENT_FUNCTION,
17211 OOMPH_EXCEPTION_LOCATION);
17212 }
17213
17214#endif // #ifdef PARANOID
17215
17216 // Get the node pointer
17217 Node* node_pt = global_shared_node_pt[i];
17218
17219#ifdef PARANOID
17220 if (node_pt == 0)
17221 {
17222 std::ostringstream error_message;
17223 error_message << "There is not global shared node within this\n"
17224 << "global node number (" << i
17225 << "). The global shared\n"
17226 << "node pointer is null\n\n";
17227 throw OomphLibError(error_message.str(),
17228 OOMPH_CURRENT_FUNCTION,
17229 OOMPH_EXCEPTION_LOCATION);
17230 }
17231#endif // #ifdef PARANOID
17232
17233 // Add the node to the nodes on shared boundaries in this
17234 // processor
17235 node_on_shd_bnd_pt[jproc].insert(node_pt);
17236
17237 // And store the global node index
17238 node_pt_to_global_shd_bnd_index[node_pt] = i;
17239 }
17240
17241 } // for (j < n_names)
17242
17243 } // for (i < n_nodes_on_shd_bnds)
17244
17245 // ---------------------------------------------------------
17246 // END: Get the shared nodes between each of processors
17247 // ---------------------------------------------------------
17248
17249 // ---------------------------------------------------------
17250 // BEGIN: Get the original boundaries associated to each
17251 // node on a shared boundary
17252 // ---------------------------------------------------------
17253
17254 // Store the global shared node number
17255 Vector<Vector<unsigned>> global_node_on_shared_bound(nproc);
17256 // Store the boundaries associated with the global shared node
17257 // number
17258 Vector<Vector<Vector<unsigned>>> global_node_original_boundaries(nproc);
17259 // Store the zeta boundary coordinate of the nodes on original
17260 // boundaries
17261 Vector<Vector<Vector<double>>> global_node_zeta_coordinate(nproc);
17262
17263 // loop over the processors
17264 for (unsigned iproc = 0; iproc < nproc; iproc++)
17265 {
17266 // Get the nodes added to be shared with the iproc processor
17267 std::set<Node*> nodes_shared_pt = node_on_shd_bnd_pt[iproc];
17268
17269 // loop over the nodes
17270 for (std::set<Node*>::iterator it = nodes_shared_pt.begin();
17271 it != nodes_shared_pt.end();
17272 it++)
17273 {
17274 // Get the node
17275 Node* node_pt = (*it);
17276 // Store the boundaries on which it is stored
17277 Vector<unsigned> on_original_boundaries;
17278 // For each boundary get the corresponding z value of the node
17279 // on the boundary
17280 Vector<double> zeta_coordinate;
17281 // Get the number of boudandaries
17282 const unsigned n_bnd = this->initial_shared_boundary_id();
17283 // loop over the boundaries and register the boundaries to which
17284 // it is associated
17285 for (unsigned bb = 0; bb < n_bnd; bb++)
17286 {
17287 // Is the node on original boundary bb?
17288 if (node_pt->is_on_boundary(bb))
17289 {
17290 // Then save it as being on boundary bb
17291 on_original_boundaries.push_back(bb);
17292 // Get the boundary coordinate
17293 Vector<double> zeta(1);
17294 node_pt->get_coordinates_on_boundary(bb, zeta);
17295 // Save the boundary coordinate
17296 zeta_coordinate.push_back(zeta[0]);
17297 }
17298
17299 } // for (bb < n_bnd)
17300
17301 // Is the node on an original boundary
17302 if (on_original_boundaries.size() > 0)
17303 {
17304 // Get the global shared node number
17305 std::map<Node*, unsigned>::iterator it_index =
17306 node_pt_to_global_shd_bnd_index.find(node_pt);
17307#ifdef PARANOID
17308 if (it_index == node_pt_to_global_shd_bnd_index.end())
17309 {
17310 std::ostringstream error_message;
17311 error_message
17312 << "We could not find the global shared node index associated\n"
17313 << "with the node pointer with vertices coordinates:\n"
17314 << "(" << node_pt->x(0) << ", " << node_pt->x(1) << ")\n\n";
17315 throw OomphLibError(error_message.str(),
17316 OOMPH_CURRENT_FUNCTION,
17317 OOMPH_EXCEPTION_LOCATION);
17318 }
17319#endif
17320 // The global shared node index
17321 const unsigned global_shared_node_number = (*it_index).second;
17322 // Store the global shared node number
17323 global_node_on_shared_bound[iproc].push_back(
17324 global_shared_node_number);
17325 // And store the original boundaries to which it is associated
17326 global_node_original_boundaries[iproc].push_back(
17327 on_original_boundaries);
17328 // and the corresponding zeta coordinate
17329 global_node_zeta_coordinate[iproc].push_back(zeta_coordinate);
17330 }
17331
17332 } // loop over nodes on shared boundaries with iproc
17333
17334 } // for (iproc < nproc)
17335
17336 // ---------------------------------------------------------
17337 // END: Get the original boundaries associated to each
17338 // node on a shared boundary
17339 // ---------------------------------------------------------
17340
17341 // ---------------------------------------------------------
17342 // BEGIN: Send the info. to the corresponding processors,
17343 // package the info, send it and receive it in the
17344 // corresponding processor, unpackage and set the
17345 // boundaries associated with the received nodes
17346 // ---------------------------------------------------------
17347
17348 // Get the communicator of the mesh
17349 OomphCommunicator* comm_pt = this->communicator_pt();
17350
17351 // Set MPI info
17352 MPI_Status status;
17353 MPI_Request request;
17354
17355 // loop over the processors
17356 for (unsigned iproc = 0; iproc < nproc; iproc++)
17357 {
17358 // The number of nodes shared between the pair of processors
17359 const unsigned n_shd_nodes_my_rank_iproc =
17360 node_on_shd_bnd_pt[iproc].size();
17361
17362 // Are there shared nodes between these pair of processors
17363 // (my_rank, iproc)? Also ensure not to send info. within myself
17364 if (n_shd_nodes_my_rank_iproc > 0 && iproc != my_rank)
17365 {
17366 // The flat package to send the info, to the iproc processor
17367 Vector<unsigned> flat_package_unsigned_send;
17368 // The very first entry is the number of nodes shared by the
17369 // pair of processors (my_rank, iproc)
17370 flat_package_unsigned_send.push_back(n_shd_nodes_my_rank_iproc);
17371
17372 // Get the number of shared nodes on original boundaries
17373 const unsigned n_global_shared_node_on_original_boundary =
17374 global_node_on_shared_bound[iproc].size();
17375
17376 // The second data is the number of shared nodes on original
17377 // boundaries
17378 flat_package_unsigned_send.push_back(
17379 n_global_shared_node_on_original_boundary);
17380
17381 // ... also send the zeta coordinates associated with the
17382 // original boundaries
17383 Vector<double> flat_package_double_send;
17384
17385 // loop over the nodes shared between this pair of processors
17386 for (unsigned i = 0; i < n_global_shared_node_on_original_boundary; i++)
17387 {
17388 // Get the global shared node index
17389 const unsigned global_shared_node_index =
17390 global_node_on_shared_bound[iproc][i];
17391
17392 // Put in the package the shared node index of the current
17393 // node
17394 flat_package_unsigned_send.push_back(global_shared_node_index);
17395
17396 // Get the original boundaries to which the node is associated
17397 Vector<unsigned> on_original_boundaries =
17398 global_node_original_boundaries[iproc][i];
17399
17400 // Get the associated zeta boundary coordinates
17401 Vector<double> zeta_coordinate =
17402 global_node_zeta_coordinate[iproc][i];
17403
17404 // Get the number of original boundaries to which the node is
17405 // associated
17406 const unsigned n_original_boundaries = on_original_boundaries.size();
17407
17408 // Put in the package the number of original boundaries the
17409 // node is associated
17410 flat_package_unsigned_send.push_back(n_original_boundaries);
17411
17412 // loop over the original boundaries ids and include them in
17413 // the package
17414 for (unsigned j = 0; j < n_original_boundaries; j++)
17415 {
17416 // Put in the package each of the original boundaries to
17417 // which it is associated
17418 flat_package_unsigned_send.push_back(on_original_boundaries[j]);
17419 // The zeta coordinate on the boundary
17420 flat_package_double_send.push_back(zeta_coordinate[j]);
17421 } // for (j < n_original_boundaries)
17422
17423 } // for (i < n_global_shared_node_on_original_boundary)
17424
17425 // Send data UNSIGNED -----------------------------------------
17426 // Get the size of the package to communicate to the iproc
17427 // processor
17428 const unsigned n_udata_send = flat_package_unsigned_send.size();
17429 int n_udata_send_int = n_udata_send;
17430
17431 // Send/receive data to/from iproc processor
17432 MPI_Isend(&n_udata_send_int,
17433 1,
17434 MPI_UNSIGNED,
17435 iproc,
17436 1,
17437 comm_pt->mpi_comm(),
17438 &request);
17439
17440 int n_udata_received_int = 0;
17441 MPI_Recv(&n_udata_received_int,
17442 1,
17443 MPI_UNSIGNED,
17444 iproc,
17445 1,
17446 comm_pt->mpi_comm(),
17447 &status);
17448 MPI_Wait(&request, MPI_STATUS_IGNORE);
17449
17450 if (n_udata_send != 0)
17451 {
17452 MPI_Isend(&flat_package_unsigned_send[0],
17453 n_udata_send,
17454 MPI_UNSIGNED,
17455 iproc,
17456 2,
17457 comm_pt->mpi_comm(),
17458 &request);
17459 }
17460
17461 const unsigned n_udata_received =
17462 static_cast<unsigned>(n_udata_received_int);
17463
17464 // Where to receive the data from the iproc processor
17465 Vector<unsigned> flat_package_unsigned_receive(n_udata_received);
17466
17467 if (n_udata_received != 0)
17468 {
17469 MPI_Recv(&flat_package_unsigned_receive[0],
17470 n_udata_received,
17471 MPI_UNSIGNED,
17472 iproc,
17473 2,
17474 comm_pt->mpi_comm(),
17475 &status);
17476 }
17477
17478 if (n_udata_send != 0)
17479 {
17480 MPI_Wait(&request, MPI_STATUS_IGNORE);
17481 }
17482
17483 // Send data DOUBLE -----------------------------------------
17484 // Get the size of the package to communicate to the iproc
17485 // processor
17486 const unsigned n_ddata_send = flat_package_double_send.size();
17487 int n_ddata_send_int = n_ddata_send;
17488
17489 // Send/receive data to/from iproc processor
17490 MPI_Isend(&n_ddata_send_int,
17491 1,
17492 MPI_UNSIGNED,
17493 iproc,
17494 1,
17495 comm_pt->mpi_comm(),
17496 &request);
17497
17498 int n_ddata_received_int = 0;
17499 MPI_Recv(&n_ddata_received_int,
17500 1,
17501 MPI_UNSIGNED,
17502 iproc,
17503 1,
17504 comm_pt->mpi_comm(),
17505 &status);
17506 MPI_Wait(&request, MPI_STATUS_IGNORE);
17507
17508 if (n_ddata_send != 0)
17509 {
17510 MPI_Isend(&flat_package_double_send[0],
17511 n_ddata_send,
17512 MPI_DOUBLE,
17513 iproc,
17514 2,
17515 comm_pt->mpi_comm(),
17516 &request);
17517 }
17518
17519 const unsigned n_ddata_received =
17520 static_cast<unsigned>(n_ddata_received_int);
17521
17522 // Where to receive the data from the iproc processor
17523 Vector<double> flat_package_double_receive(n_ddata_received);
17524
17525 if (n_ddata_received != 0)
17526 {
17527 MPI_Recv(&flat_package_double_receive[0],
17528 n_ddata_received,
17529 MPI_DOUBLE,
17530 iproc,
17531 2,
17532 comm_pt->mpi_comm(),
17533 &status);
17534 }
17535
17536 if (n_ddata_send != 0)
17537 {
17538 MPI_Wait(&request, MPI_STATUS_IGNORE);
17539 }
17540
17541 // Unpackage -------------------------------------------------
17542 // ... and associate the nodes to the corresponding original
17543 // boundaries
17544
17545 // The number of nodes to be received
17546 unsigned n_shared_nodes_received = flat_package_unsigned_receive[0];
17547
17548 // Increase and decrease the number of received shared nodes to
17549 // avoid the warning when compiling without PARANOID
17550 n_shared_nodes_received++;
17551 n_shared_nodes_received--;
17552
17553#ifdef PARANOID
17554 if (n_shd_nodes_my_rank_iproc != n_shared_nodes_received)
17555 {
17556 std::ostringstream error_message;
17557 error_message
17558 << "The number of shared nodes between the pair of processors is\n"
17559 << "not the same\n"
17560 << "N.shared nodes proc (" << my_rank << ") with proc (" << iproc
17561 << "): (" << n_shd_nodes_my_rank_iproc << "\n"
17562 << "N.shared nodes proc (" << iproc << ") with proc (" << my_rank
17563 << "): (" << n_shared_nodes_received << "\n\n"
17564 << "You should have got the same error in proc: (" << iproc
17565 << ")\n\n";
17566 throw OomphLibError(error_message.str(),
17567 OOMPH_CURRENT_FUNCTION,
17568 OOMPH_EXCEPTION_LOCATION);
17569 } // if (n_shd_nodes_my_rank_iproc != n_shared_nodes_received)
17570#endif
17571
17572 // Skip the number of nodes on shared boundaries on original
17573 // boundaries received (that is why next lines are commented)
17574
17575 // The number of nodes on shared boundaries on original
17576 // boundaries
17577 // const unsigned n_shared_nodes_on_original_boundaries_received =
17578 // flat_package_unsigned_receive[1];
17579
17580 // loop over the received info.
17581 unsigned current_index_data = 2;
17582 unsigned current_index_ddata = 0;
17583 while (current_index_data < n_udata_received)
17584 {
17585 // The global shared node number
17586 const unsigned global_shared_node_index =
17587 flat_package_unsigned_receive[current_index_data++];
17588
17589 // The pointer to the node
17590 Node* node_pt = 0;
17591
17592 // The number of original boundaries the node is associated
17593 // with
17594 const unsigned n_original_boundaries =
17595 flat_package_unsigned_receive[current_index_data++];
17596
17597 // Get the node pointer
17598 node_pt = global_shared_node_pt[global_shared_node_index];
17599#ifdef PARANOID
17600 if (node_pt == 0)
17601 {
17602 std::ostringstream error_message;
17603 error_message
17604 << "The global shared node (" << global_shared_node_index << ") "
17605 << "could not be found in this processor!!!\n"
17606 << "However, it was found in processor (" << iproc << "). The "
17607 << "data may be no synchronised,\ntherefore "
17608 << "we may be looking for a global shared node number that "
17609 << "do not\ncorrespond with the one that was sent by "
17610 << "processor (" << iproc << ")\n\n";
17611 throw OomphLibError(error_message.str(),
17612 OOMPH_CURRENT_FUNCTION,
17613 OOMPH_EXCEPTION_LOCATION);
17614 }
17615#endif // #ifdef PARANOID
17616
17617 // loop over the number of original boundaries and associate
17618 // the node to each of those boundaries
17619 for (unsigned i = 0; i < n_original_boundaries; i++)
17620 {
17621 // Get the original boundary to which the node is associated
17622 // with
17623 const unsigned original_bound_id =
17624 flat_package_unsigned_receive[current_index_data++];
17625
17626 // Associate the node with the boundary
17627 this->add_boundary_node(original_bound_id, node_pt);
17628
17629 // Get the zeta boundary coordinate
17630 Vector<double> zeta(1);
17631 zeta[0] = flat_package_double_receive[current_index_ddata++];
17632 node_pt->set_coordinates_on_boundary(original_bound_id, zeta);
17633 }
17634
17635 } // while(current_data < n_data_received)
17636
17637 } // if ((node_on_shd_bnd_pt(iproc) > 0) && iproc!=my_rank)
17638
17639 } // for (iproc < nproc)
17640
17641 // ---------------------------------------------------------
17642 // END: Send the info. to the corresponding processors,
17643 // package the info, send it and receive it in the
17644 // corresponding processor, unpackage and set the
17645 // boundaries associated with the received nodes
17646 // ---------------------------------------------------------
17647 }
17648
17649 //======================================================================
17650 // In charge of creating additional halo(ed) elements on those
17651 // processors that have no shared boundaries in common but have
17652 // shared nodes
17653 // ======================================================================
17654 template<class ELEMENT>
17656 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
17657 other_proc_shd_bnd_node_pt,
17658 Vector<Vector<Node*>>& iproc_currently_created_nodes_pt,
17659 Vector<Vector<Vector<unsigned>>>& global_node_names,
17660 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
17661 Vector<Node*>& global_shared_node_pt)
17662 {
17663 // Get the rank and number of processors
17664 const unsigned nproc = this->communicator_pt()->nproc();
17665 const unsigned my_rank = this->communicator_pt()->my_rank();
17666
17667 // ---------------------------------------------------------------
17668 // BEGIN: Create a map to check whether a node is on the global
17669 // shared nodes. Also set a map to obtain the global
17670 // shared node index (this index is the same as the global
17671 // node name)
17672 // ---------------------------------------------------------------
17673 std::map<Node*, bool> is_global_shared_node;
17674 std::map<Node*, unsigned> global_shared_node_index;
17675
17676 // Get the number of global shared nodes
17677 const unsigned n_global_shared_nodes = global_shared_node_pt.size();
17678 // loop over the global shared nodes
17679 for (unsigned i = 0; i < n_global_shared_nodes; i++)
17680 {
17681 // Get the node
17682 Node* node_pt = global_shared_node_pt[i];
17683 // Indicate this is a shared global node
17684 is_global_shared_node[node_pt] = true;
17685 // Set the map to obtain the index of the global shared node
17686 global_shared_node_index[node_pt] = i;
17687
17688 } // for (i < n_global_shared_nodes)
17689
17690 // ---------------------------------------------------------------
17691 // END: Create a map to check whether a node is on the global
17692 // shared nodes. Also set a map to obtain the global
17693 // shared node index (this index is the same as the global
17694 // node name)
17695 // ---------------------------------------------------------------
17696
17697 // ---------------------------------------------------------------
17698 // BEGIN: Loop over the haloed elements and check whether the nodes
17699 // on the haloed elements are part of the global shared
17700 // nodes. If that is the case then check whether the
17701 // element should be sent to the processors with which the
17702 // node is shared
17703 // ---------------------------------------------------------------
17704
17705 // Elements that may be sent to other processors
17706 Vector<std::set<GeneralisedElement*>> additional_elements_pt(nproc);
17707
17708 // loop over the processors
17709 for (unsigned iproc = 0; iproc < nproc; iproc++)
17710 {
17711 if (iproc != my_rank)
17712 {
17713 // Get the haloed element with iproc
17714 Vector<GeneralisedElement*> haloed_ele_pt =
17715 this->root_haloed_element_pt(iproc);
17716
17717 // Get the number of haloed elements
17718 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17719
17720 // loop over the haloed elements with iproc
17721 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17722 {
17723 // A pointer to the generalised element
17724 GeneralisedElement* gele_pt = haloed_ele_pt[ihd];
17725 // Get the finite element representation of the element
17726 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17727 // Get the number of nodes
17728 const unsigned n_nodes = ele_pt->nnode();
17729 // loop over the nodes of the element
17730 for (unsigned n = 0; n < n_nodes; n++)
17731 {
17732 // Get the node
17733 Node* node_pt = ele_pt->node_pt(n);
17734 // Is the node a global shared node?
17735 if (is_global_shared_node[node_pt])
17736 {
17737 // Get the index of the global shared node
17738 const unsigned global_index = global_shared_node_index[node_pt];
17739 // Get the global names of the node
17740 Vector<Vector<unsigned>> iglobal_names =
17741 global_node_names[global_index];
17742
17743 // Get the number of names
17744 const unsigned n_names = iglobal_names.size();
17745 // loop over the names and check which processors share
17746 // this node (the processors to which the element may be
17747 // sent
17748 for (unsigned j = 0; j < n_names; j++)
17749 {
17750 // Get the processors to which the element should be
17751 // sent
17752 const unsigned proc1 = iglobal_names[j][0];
17753 const unsigned proc2 = iglobal_names[j][1];
17754 // Add the element to the set of additional elements to
17755 // sent from proc1 to proc2
17756 additional_elements_pt[proc1].insert(gele_pt);
17757 additional_elements_pt[proc2].insert(gele_pt);
17758
17759 } // for (j < n_names)
17760
17761 } // if (is_global_shared_node[node_pt])
17762
17763 } // for (n < n_nodes)
17764
17765 } // for (ihd < n_haloed_ele)
17766
17767 } // if (iproc!=my_rank)
17768
17769 } // for (iproc < nproc)
17770
17771 // ---------------------------------------------------------------
17772 // Now check whether the element should really be sent to the
17773 // indicated processors
17774
17775 // The elements from this (my_rank) processor that will be sent to
17776 // other processors
17777 Vector<Vector<FiniteElement*>> send_haloed_ele_pt(nproc);
17778
17779 // loop over the processors
17780 for (unsigned iproc = 0; iproc < nproc; iproc++)
17781 {
17782 if (iproc != my_rank)
17783 {
17784 // Get the set of element that may be sent to the iproc
17785 // processor
17786 std::set<GeneralisedElement*> iproc_ele_pt =
17787 additional_elements_pt[iproc];
17788 // loop over the element that may be sent to the iproc
17789 // processor
17790 for (std::set<GeneralisedElement*>::iterator it = iproc_ele_pt.begin();
17791 it != iproc_ele_pt.end();
17792 it++)
17793 {
17794 // Get a pointer to the element
17795 GeneralisedElement* gele_pt = (*it);
17796
17797 // Get the haloed element with iproc
17798 Vector<GeneralisedElement*> haloed_ele_pt =
17799 this->root_haloed_element_pt(iproc);
17800
17801 // Get the number of haloed elements
17802 const unsigned n_haloed_ele = this->nroot_haloed_element(iproc);
17803
17804 // Flag to indicate whether the element has been already sent
17805 // to the iproc processor
17806 bool send_ele_to_iproc_processor = true;
17807 // loop over the haloed elements with iproc and check whether
17808 // the element has been already sent to iproc (if it is
17809 // already a haloed element with iproc then it has been
17810 // already sent)
17811 for (unsigned ihd = 0; ihd < n_haloed_ele; ihd++)
17812 {
17813 // A pointer to the generalised element
17814 GeneralisedElement* ghd_ele_pt = haloed_ele_pt[ihd];
17815 if (gele_pt == ghd_ele_pt)
17816 {
17817 // Mark the element as not required to be sent
17818 send_ele_to_iproc_processor = false;
17819 // Break the loop that searchs for the element on the
17820 // haloed elements with iproc
17821 break;
17822 }
17823
17824 } // for (ihd < n_haloed_ele)
17825
17826 // Do we need to sent the element?
17827 if (send_ele_to_iproc_processor)
17828 {
17829 // Get the finite element representation of the element
17830 FiniteElement* ele_pt = dynamic_cast<FiniteElement*>(gele_pt);
17831 // Add the element to those that will be sent to the iproc
17832 // processor
17833 send_haloed_ele_pt[iproc].push_back(ele_pt);
17834 }
17835
17836 } // loop over the elements that may be sent to the iproc
17837 // processor
17838
17839 } // if (iproc!=my_rank)
17840
17841 } // for (iproc < nproc)
17842
17843 // ---------------------------------------------------------------
17844 // END: Loop over the haloed element and check whether the nodes
17845 // on the haloed elements are part of the global shared
17846 // nodes. If that is the case then check whether the element
17847 // should be sent to the processors with which the node is
17848 // shared
17849 // ---------------------------------------------------------------
17850
17851 // ============================================================
17852 // Now send the additional elements
17853 // ============================================================
17854 // Loop over the processors to send data
17855 for (unsigned iproc = 0; iproc < nproc; iproc++)
17856 {
17857 // There are no elements to send with myself
17858 if (iproc != my_rank)
17859 {
17860 // Get the number of additional haloed elements to send
17861 const unsigned n_additional_haloed_ele =
17862 send_haloed_ele_pt[iproc].size();
17863
17864 // Clear send and receive buffers
17865 Flat_packed_unsigneds.clear();
17866 Flat_packed_doubles.clear();
17867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17869#endif
17870
17871 // The very first data of the flat packed is the number of
17872 // additional haloed elements, this will be the number of
17873 // additional halo elements to create on the receiver processor
17874 Flat_packed_unsigneds.push_back(n_additional_haloed_ele);
17875#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17876 std::stringstream junk;
17877 junk << "Number of haloed elements " << nhaloed_ele;
17878 Flat_packed_unsigneds_string.push_back(junk.str());
17879#endif
17880
17881 // Loop over the additioanl haloed elements
17882 for (unsigned e = 0; e < n_additional_haloed_ele; e++)
17883 {
17884 // Get pointer to the additional haloed element
17885 FiniteElement* ele_pt = send_haloed_ele_pt[iproc][e];
17886 const unsigned nroot_haloed_ele = this->nroot_haloed_element(iproc);
17887
17888 // Check if the element has been already added to the
17889 // halo(ed) scheme
17890
17891 // Get the generalised version of the element
17892 GeneralisedElement* gen_ele_pt = ele_pt;
17893 // Try to add the haloed element
17894 const unsigned haloed_ele_index =
17895 this->try_to_add_root_haloed_element_pt(iproc, gen_ele_pt);
17896
17897 // Was the element added or only returned the index of the
17898 // element
17899 if (nroot_haloed_ele == haloed_ele_index)
17900 {
17901 Flat_packed_unsigneds.push_back(1);
17902#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17904 "Haloed element needs to be constructed");
17905#endif
17906
17907 // Get additional info. related with the haloed element
17908 get_required_elemental_information_helper(iproc, ele_pt);
17909
17910 // Get the nodes on the element
17911 const unsigned nnodes = ele_pt->nnode();
17912 for (unsigned j = 0; j < nnodes; j++)
17913 {
17914 Node* node_pt = ele_pt->node_pt(j);
17915
17916 // Package the info. of the nodes
17917 // The destination processor goes in the arguments
17918 add_haloed_node_helper(iproc, node_pt);
17919
17920 } // for (j < nnodes)
17921
17922 } // add the element and send its nodes
17923 else // The haloed element already exists
17924 {
17925 Flat_packed_unsigneds.push_back(0);
17926#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17928 "Haloed element already exists");
17929#endif
17930 Flat_packed_unsigneds.push_back(haloed_ele_index);
17931#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17933 "Index of existing haloed element");
17934#endif
17935 } // else (next_haloed_ele == external_haloed_ele_index)
17936
17937 } // for (e < n_additional_haloed_ele)
17938
17939 // Send and received the additional haloed elements (all
17940 // processors send and receive)
17941
17942 // The processor to which send the elements
17943 int send_proc = static_cast<int>(iproc);
17944 // The processor from which receive the elements
17945 int recv_proc = static_cast<int>(iproc);
17946 send_and_receive_elements_nodes_info(send_proc, recv_proc);
17947
17948 // Reset the counters
17951
17952 // Get the number of additional halo element to be created
17953 const unsigned n_additional_halo_ele =
17955
17956#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
17958 << " Number of elements need to be constructed "
17960 << std::endl;
17961#endif
17962
17963 // Create the additional halo elements
17964 for (unsigned e = 0; e < n_additional_halo_ele; e++)
17965 {
17966 // Create halo element from received info. of "iproc"
17967 // processor on the current processor
17968 create_halo_element(iproc,
17969 iproc_currently_created_nodes_pt[iproc],
17970 other_proc_shd_bnd_node_pt,
17971 global_node_names,
17972 node_name_to_global_index,
17973 global_shared_node_pt);
17974
17975 } // for (e < n_additional_halo_ele)
17976
17977 } // if (iproc != my_rank)
17978
17979 } // for (iproc < nproc)
17980 }
17981
17982 // *********************************************************************
17983 // Start communication functions
17984 // *********************************************************************
17985
17986 //========start of get_required_elemental_information_helper==============
17987 /// Helper function to get the required elemental information from
17988 /// an haloed element. This info. involves the association of the element
17989 /// to a boundary or region.
17990 //========================================================================
17991 template<class ELEMENT>
17993 ELEMENT>::get_required_elemental_information_helper(unsigned& iproc,
17994 FiniteElement* ele_pt)
17995 {
17996 // Check if the element is associated with the original boundaries
17997 const unsigned nbound = this->initial_shared_boundary_id();
17998
17999 // ------------------------------------------------------------------
18000 // Stores the information regarding the boundaries associated to the
18001 // element (it that is the case)
18002 Vector<unsigned> associated_boundaries;
18003 Vector<unsigned> face_index_on_boundary;
18004
18005 unsigned counter_face_indexes = 0;
18006
18007 for (unsigned b = 0; b < nbound; b++)
18008 {
18009 // Get the number of elements associated to boundary i
18010 const unsigned nboundary_ele = nboundary_element(b);
18011 for (unsigned e = 0; e < nboundary_ele; e++)
18012 {
18013 if (ele_pt == this->boundary_element_pt(b, e))
18014 {
18015 // Keep track of the boundaries associated to the element
18016 associated_boundaries.push_back(b);
18017 // Get the face index
18018 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
18019 counter_face_indexes++;
18020#ifdef PARANOID
18021 if (counter_face_indexes > 2)
18022 {
18023 std::stringstream error_message;
18024 error_message
18025 << "A triangular element can not have more than two of its faces "
18026 << "on a boundary!!!\n\n";
18027 throw OomphLibError(error_message.str(),
18028 OOMPH_CURRENT_FUNCTION,
18029 OOMPH_EXCEPTION_LOCATION);
18030 }
18031#else
18032 // Already found 2 face indexes on the same boundary?
18033 if (counter_face_indexes == 2)
18034 {
18035 break;
18036 }
18037#endif // #ifdef PARANOID
18038
18039 } // if (ele_pt == this->boundary_element_pt(b,e))
18040
18041 } // (e < nboundary_ele)
18042
18043 } // (b < nbound)
18044
18045 // If the element is associated to any boundary then package all the
18046 // relevant info
18047 const unsigned nassociated_boundaries = associated_boundaries.size();
18048 if (nassociated_boundaries > 0)
18049 {
18050 Flat_packed_unsigneds.push_back(1);
18051#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18053 "The element is a boundary element");
18054#endif
18055 Flat_packed_unsigneds.push_back(nassociated_boundaries);
18056#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18057 std::stringstream junk;
18058 junk << "The elements is associated to " << nassociated_boundaries
18059 << " boundaries";
18060 Flat_packed_unsigneds_string.push_back(junk.str());
18061#endif
18062
18063 // Package the ids of the associated boundaries and the
18064 // corresponding face index for each boundary (if the element is a
18065 // corner element, it will have two faces associated to the
18066 // boundary)
18067 for (unsigned i = 0; i < nassociated_boundaries; i++)
18068 {
18069 unsigned b = associated_boundaries[i];
18070 Flat_packed_unsigneds.push_back(b);
18071#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18072 std::stringstream junk;
18073 junk << "Element associated to boundary " << b << " of "
18074 << nassociated_boundaries << " total associated boundaries";
18075 Flat_packed_unsigneds_string.push_back(junk.str());
18076#endif
18077 unsigned f = face_index_on_boundary[i];
18078 Flat_packed_unsigneds.push_back(f);
18079#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18080 std::stringstream junk2;
18081 junk2 << "Face index " << f << " for associated boundary " << b;
18082 Flat_packed_unsigneds_string.push_back(junk2.str());
18083#endif
18084 }
18085
18086 // If the element is associated to any boundary then we should
18087 // check if the mesh has regions, if that is the case then we need
18088 // to check to which region the boundary element does belong
18089
18090 // If the mesh has regions we should look for the element
18091 // associated to a boundary and a specified region
18092 Vector<Vector<unsigned>> associated_boundaries_and_regions;
18093 Vector<unsigned> face_index_on_boundary_and_region;
18094
18095 // Now check for the case when we have regions in the mesh
18096 const unsigned n_regions = this->nregion();
18097 if (n_regions > 1)
18098 {
18099 // Used to count the number of faces associated with
18100 // boundary-regions
18101 unsigned counter_face_indexes_in_regions = 0;
18102 // Loop over the boundaries
18103 for (unsigned b = 0; b < nbound; b++)
18104 {
18105 // Go through each region by getting the region id
18106 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
18107 {
18108 // Get thre region id associated with the (i_reg)-th region
18109 const unsigned region_id =
18110 static_cast<unsigned>(this->Region_attribute[i_reg]);
18111
18112 // Loop over all elements associated with the current boundary
18113 // and the i_reg-th region and check if the element is part of
18114 // any region
18115 const unsigned nele_in_region =
18116 this->nboundary_element_in_region(b, region_id);
18117 for (unsigned ee = 0; ee < nele_in_region; ee++)
18118 {
18119 // Check if the boundary-region element is the same as the
18120 // element
18121 if (ele_pt ==
18122 this->boundary_element_in_region_pt(b, region_id, ee))
18123 {
18124 // Storage for the boundary and region associated to the
18125 // element
18126 Vector<unsigned> bound_and_region(2);
18127
18128 // Keep track of the boundaries associated to the element
18129 bound_and_region[0] = b;
18130 // Keep track of the regions associated to the element
18131 bound_and_region[1] = region_id;
18132 // Add the boundaries and regions in the storage to be
18133 // sent to other processors
18134 associated_boundaries_and_regions.push_back(bound_and_region);
18135 // Get the face index and keep track of it
18136 face_index_on_boundary_and_region.push_back(
18137 this->face_index_at_boundary_in_region(b, region_id, ee));
18138
18139 // Increase the number of faces of the element associated
18140 // to boundary-regions
18141 counter_face_indexes_in_regions++;
18142
18143#ifdef PARANOID
18144 if (counter_face_indexes_in_regions > 2)
18145 {
18146 std::stringstream error_message;
18147 error_message << "A triangular element can not have more "
18148 "than two of its\n"
18149 << "faces on a boundary!!!\n\n";
18150 throw OomphLibError(error_message.str(),
18151 OOMPH_CURRENT_FUNCTION,
18152 OOMPH_EXCEPTION_LOCATION);
18153 } // if (counter_face_indexes_in_regions > 2)
18154#endif
18155
18156 } // The element is a boundary-region element
18157
18158 } // for (ee < nele_in_region)
18159
18160 } // for (i_reg < n_regions)
18161
18162 } // for (b < nbound)
18163
18164 } // if (n_regions > 1)
18165
18166 // Now package the info. to be sent to other processors
18167 const unsigned nassociated_boundaries_and_regions =
18168 associated_boundaries_and_regions.size();
18169 if (nassociated_boundaries_and_regions > 0)
18170 {
18171 Flat_packed_unsigneds.push_back(1);
18172#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18174 "The element is associated to boundaries and regions");
18175#endif
18176
18177 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
18178#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18179 std::stringstream junk;
18180 junk << "The element is associated to "
18181 << nassociated_boundaries_and_regions << " boundaries-regions";
18182 Flat_packed_unsigneds_string.push_back(junk.str());
18183#endif
18184
18185 // Package the ids of the associated boundaries, regions and the
18186 // corresponding face index for each boundary-region (if the
18187 // element is a corner element, it will have two faces
18188 // associated to the boundary-region)
18189 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
18190 {
18191 const unsigned b = associated_boundaries_and_regions[i][0];
18192 Flat_packed_unsigneds.push_back(b);
18193#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18194 std::stringstream junk;
18195 junk << "Element associated to boundary " << b << " of "
18196 << nassociated_boundaries_and_regions
18197 << " total associated boundaries-regions";
18198 Flat_packed_unsigneds_string.push_back(junk.str());
18199#endif
18200
18201 const unsigned r = associated_boundaries_and_regions[i][1];
18202 Flat_packed_unsigneds.push_back(r);
18203#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18204 std::stringstream junk2;
18205 junk2 << "Element associated to region " << r << " of "
18206 << nassociated_boundaries_and_regions
18207 << " total associated boundaries-regions";
18208 Flat_packed_unsigneds_string.push_back(junk2.str());
18209#endif
18210
18211 const unsigned f = face_index_on_boundary_and_region[i];
18212 Flat_packed_unsigneds.push_back(f);
18213#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18214 std::stringstream junk3;
18215 junk3 << "Face index " << f << " for associated boundary-region ("
18216 << b << "-" << r << ")";
18217 Flat_packed_unsigneds_string.push_back(junk3.str());
18218#endif
18219 } // for (i < nassociated_boundaries_and_regions)
18220 } // if (nassociated_boundaries_and_regions > 0)
18221 else
18222 {
18223 Flat_packed_unsigneds.push_back(0);
18224#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18226 "The element is NOT associated to boundaries and regions");
18227#endif
18228 } // else if (nassociated_boundaries_and_regions > 0)
18229 }
18230 else
18231 {
18232 Flat_packed_unsigneds.push_back(0);
18233#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18235 "The element is not associated to any original boundary");
18236#endif
18237 }
18238
18239 // ------------------------------------------------------------
18240 // Now review if the element is associated to a shared boundary
18241
18242 // Store the shared boundaries, and therefore the face indexes
18243 // associated to the element
18244 Vector<unsigned> associated_shared_boundaries;
18245 Vector<unsigned> face_index_on_shared_boundary;
18246
18247 // Get the shared boundaries in this processor
18248 Vector<unsigned> my_rank_shared_boundaries_ids;
18249 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
18250
18251 // Get the number of shared boundaries
18252 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
18253 // Loop over the shared boundaries
18254 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
18255 {
18256 // Get the boundary id
18257 const unsigned sb = my_rank_shared_boundaries_ids[i];
18258
18259 // Get the number of elements associated to shared boundary sb
18260 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
18261 for (unsigned e = 0; e < nboundary_ele; e++)
18262 {
18263 if (ele_pt == this->shared_boundary_element_pt(sb, e))
18264 {
18265 // Keep track of the boundaries associated to the element
18266 associated_shared_boundaries.push_back(sb);
18267 // Get the face index
18268 face_index_on_shared_boundary.push_back(
18269 this->face_index_at_shared_boundary(sb, e));
18270 }
18271 } // (e < nboundary_ele)
18272 } // (i < nmy_rank_shd_bnd)
18273
18274 // If the element is associated to a shared boundary then package
18275 // all the relevant info
18276 const unsigned nassociated_shared_boundaries =
18277 associated_shared_boundaries.size();
18278 if (nassociated_shared_boundaries > 0)
18279 {
18280 Flat_packed_unsigneds.push_back(3);
18281#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18283 "The element is a shared boundary element");
18284#endif
18285 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
18286#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18287 std::stringstream junk;
18288 junk << "The elements is associated to " << nassociated_shared_boundaries
18289 << "shared boundaries";
18290 Flat_packed_unsigneds_string.push_back(junk.str());
18291#endif
18292
18293 // Package the ids of the associated boundaries
18294 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
18295 {
18296 const unsigned b = associated_shared_boundaries[i];
18297 Flat_packed_unsigneds.push_back(b);
18298#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18299 std::stringstream junk;
18300 junk << "Element associated to shared boundary " << b << " of "
18301 << nassociated_shared_boundaries << " total associated boundaries";
18302 Flat_packed_unsigneds_string.push_back(junk.str());
18303#endif
18304
18305 const unsigned f = face_index_on_shared_boundary[i];
18306 Flat_packed_unsigneds.push_back(f);
18307#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18308 std::stringstream junk2;
18309 junk2 << "Face index " << f << " for associated shared boundary " << b;
18310 Flat_packed_unsigneds_string.push_back(junk2.str());
18311#endif
18312 }
18313 }
18314 else
18315 {
18316 Flat_packed_unsigneds.push_back(0);
18317#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18319 "The element is not associated to any shared boundary");
18320#endif
18321 }
18322 }
18323
18324 //========start of get_required_nodal_information_helper==================
18325 /// Helper function to get the required nodal information from an
18326 /// haloed node so that a fully-functional halo node (and therefore element)
18327 /// can be created on the receiving process
18328 //========================================================================
18329 template<class ELEMENT>
18331 unsigned& iproc, Node* nod_pt)
18332 {
18333 unsigned my_rank = this->communicator_pt()->my_rank();
18334 const unsigned nproc = this->communicator_pt()->nproc();
18335
18336 // Tell the halo copy of this node how many values there are
18337 // [NB this may be different for nodes within the same element, e.g.
18338 // when using Lagrange multipliers]
18339 unsigned n_val = nod_pt->nvalue();
18340 Flat_packed_unsigneds.push_back(n_val);
18341#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18342 Flat_packed_unsigneds_string.push_back("Number of values");
18343#endif
18344
18345 unsigned n_dim = nod_pt->ndim();
18346
18347 // Default number of previous values to 1
18348 unsigned n_prev = 1;
18349 if (this->Time_stepper_pt != 0)
18350 {
18351 // Add number of history values to n_prev
18352 n_prev = this->Time_stepper_pt->ntstorage();
18353 }
18354
18355 // -----------------------------------------------------
18356 // Is the node on an original boundary?
18357 // Store the original boundaries where the node may be
18358 Vector<unsigned> original_boundaries;
18359 // Loop over the original boundaries of the mesh and check if live
18360 // on one of them
18361 const unsigned n_bnd = this->initial_shared_boundary_id();
18362 for (unsigned bb = 0; bb < n_bnd; bb++)
18363 {
18364 // Which boundaries (could be more than one) is it on?
18365 if (nod_pt->is_on_boundary(bb))
18366 {
18367 original_boundaries.push_back(bb);
18368 }
18369 }
18370
18371 const unsigned n_original_boundaries = original_boundaries.size();
18372 // Is the node on any original boundary?
18373 if (n_original_boundaries > 0)
18374 {
18375 // Indicate that the node is on an original boundary
18376 Flat_packed_unsigneds.push_back(2);
18377#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18379 "Node is on the original boundaries");
18380#endif
18381
18382 Flat_packed_unsigneds.push_back(n_original_boundaries);
18383#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18384 std::stringstream junk;
18385 junk << "Node is on " << n_original_boundaries << " original boundaries";
18386 Flat_packed_unsigneds_string.push_back(junk.str());
18387#endif
18388
18389 // Loop over the original boundaries the node is on
18390 for (unsigned i = 0; i < n_original_boundaries; i++)
18391 {
18392 Flat_packed_unsigneds.push_back(original_boundaries[i]);
18393#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18394 std::stringstream junk;
18395 junk << "Node is on boundary " << original_boundaries[i] << " of "
18396 << nb;
18397 Flat_packed_unsigneds_string.push_back(junk.str());
18398#endif
18399 // Get the boundary coordinate of the node
18400 Vector<double> zeta(1);
18401 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
18402 Flat_packed_doubles.push_back(zeta[0]);
18403 }
18404 }
18405 else
18406 {
18407 // Indicate that the node is NOT on an original boundary
18408 Flat_packed_unsigneds.push_back(0);
18409#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18411 "Node is on any original boundary");
18412#endif
18413 }
18414
18415 // -------------------------------------------------------
18416 // Is the node on shared boundaries?
18417 bool node_on_shared_boundary = false;
18418 // Loop over the shared boundaries with the iproc processors and
18419 // check if live on one of them
18420 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
18421 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18422 {
18423 // Get the boundary id
18424 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18425 // Which boundaries (could be more than one) is it on?
18426 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18427 {
18428 node_on_shared_boundary = true;
18429 break;
18430 }
18431 }
18432
18433 // If the node live on any of the shared boundaries with the iproc
18434 // processor then just get the node number according to the
18435 // sorted_shared_boundary_node_pt() scheme and send it accross
18436 if (node_on_shared_boundary)
18437 {
18438 Flat_packed_unsigneds.push_back(1);
18439#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18440 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
18441#endif
18442
18443 // Store the shared boundaries where the node is on
18444 Vector<unsigned> shd_boundaries;
18445 // Loop over the shared boundaries with the iproc processor
18446 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
18447 {
18448 // Get the boundary id
18449 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
18450 // Which boundaries (could be more than one) is it on?
18451 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
18452 {
18453 shd_boundaries.push_back(i_bnd);
18454 }
18455 }
18456
18457 // Get the number of shared boundaries the node is on
18458 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
18459 // Send the number of shared boundaries the node is on
18460 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
18461#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18462 std::stringstream junk;
18463 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
18464 Flat_packed_unsigneds_string.push_back(junk.str());
18465#endif
18466
18467 // Loop over the shared boundaries to send their ids
18468 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
18469 {
18470 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
18471#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18472 std::stringstream junk;
18473 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
18474 Flat_packed_unsigneds_string.push_back(junk.str());
18475#endif
18476 }
18477
18478 // Given that the node is on at least one boundary get the index
18479 // of the node in one of the boundaries and send this index
18480 unsigned shared_boundary_id = shd_boundaries[0];
18481 // Get the number of nodes on the given shared boundary
18482 const unsigned n_nodes_on_shared_boundary =
18483 nsorted_shared_boundary_node(shared_boundary_id);
18484 // Store the index of the node on the shared boundary
18485 unsigned index_node_on_shared_boundary;
18486#ifdef PARANOID
18487 // Flag to know if the node has been found
18488 bool found_index_node_on_shared_boundary = false;
18489#endif
18490 // Loop over the nodes on the shared boundary to find the node
18491 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
18492 {
18493 // Get the i-th node on the shared boundary
18494 Node* shared_node_pt =
18495 sorted_shared_boundary_node_pt(shared_boundary_id, i);
18496 // Is the node we are looking for
18497 if (shared_node_pt == nod_pt)
18498 {
18499 // Store the index
18500 index_node_on_shared_boundary = i;
18501#ifdef PARANOID
18502 // Mark as found
18503 found_index_node_on_shared_boundary = true;
18504#endif
18505 break; // break
18506 }
18507
18508 } // for (i < nnodes_on_shared_boundary)
18509
18510#ifdef PARANOID
18511 if (!found_index_node_on_shared_boundary)
18512 {
18513 std::ostringstream error_message;
18514 error_message << "The index of the node on boundary ("
18515 << shared_boundary_id << ") was not found.\n"
18516 << "The node coordinates are (" << nod_pt->x(0) << ","
18517 << nod_pt->x(1) << ").\n";
18518 throw OomphLibError(
18519 error_message.str(),
18520 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18521 OOMPH_EXCEPTION_LOCATION);
18522 }
18523#endif
18524 // Send the index of the node on the shared boundary
18525 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
18526#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18527 std::stringstream junk2;
18528 junk2 << "Node index on boundary " << boundaries[0] << " is "
18529 << index_node_on_shared_boundary;
18530 Flat_packed_unsigneds_string.push_back(junk2.str());
18531#endif
18532
18533 } // if (node_on_shared_boundary)
18534 else
18535 {
18536 // The node is not on a shared boundary
18537 Flat_packed_unsigneds.push_back(0);
18538#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18540 "Node is not on a shared boundary");
18541#endif
18542 }
18543
18544 // ----------------------------------------------------------------
18545 // Is the node on any shared boundary where the receiver processor
18546 // is not involved?
18547
18548 // Now check if the node is on a shared boundary created by the
18549 // current processor (my_rank) and other processor different that
18550 // the iproc processor. This info. will help to complete the sending
18551 // of halo(ed) information between processors
18552
18553 // Flag to know if the node is on a shared boundary with other
18554 // processor
18555 bool node_on_shared_boundary_with_other_processors = false;
18556 // Count the number of other shared boundaries it could be on
18557 unsigned nshared_boundaries_with_other_processors_have_node = 0;
18558
18559 // Loop over the shared boundaries of the sent processor (my_rank)
18560 // and other processors (jproc)
18561 for (unsigned jproc = 0; jproc < nproc; jproc++)
18562 {
18563 // Do not search with the iproc processor , that was done before
18564 // above because we are sending info to that processor
18565 if (jproc != iproc)
18566 {
18567 // Get the number of shared boundaries with the jproc processor
18568 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18569 // Loop over the shared boundaries
18570 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18571 {
18572 // Get the boundary id
18573 const unsigned j_shd_bnd =
18574 this->shared_boundaries_ids(my_rank, jproc, bb);
18575 // Is the node part of this boundary?
18576 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18577 {
18578 // DEBP("Sending to");
18579 // DEBP(iproc);
18580 // DEBP("Pair of procs where other shared");
18581 // DEBP(my_rank);
18582 // DEBP(jproc);
18583 // DEBP(i_bnd);
18584 node_on_shared_boundary_with_other_processors = true;
18585 // Increase the counter for the number of shared boundaries
18586 // with other processors the node is on
18587 nshared_boundaries_with_other_processors_have_node++;
18588 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
18589
18590 } // for (bb<n_jshd_bnd)
18591
18592 } // if (jproc != iproc)
18593
18594 } // for (jproc < nproc)
18595
18596 // If the node is on a shared boundary with another processor
18597 // (my_rank, jproc), then send the flag and look for the info.
18598 if (node_on_shared_boundary_with_other_processors)
18599 {
18600 Flat_packed_unsigneds.push_back(4);
18601#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18603 "Node is on shared boundary no related with the received processor: 4");
18604#endif
18605
18606 // The number of packages of information that will be sent to the
18607 // "iproc" processor. This helps to know how many packages of data
18608 // read from the received processor
18609 Flat_packed_unsigneds.push_back(
18610 nshared_boundaries_with_other_processors_have_node);
18611#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18612 std::stringstream junk;
18613 junk << "Number of other shared boundaries that the node is on: "
18614 << nshared_boundaries_with_other_processors_have_node;
18615 Flat_packed_unsigneds_string.push_back(junk.str());
18616#endif
18617
18618 // Counter to ensure that the correct number of data has been sent
18619 unsigned counter_shd_bnd_with_other_procs_have_node = 0;
18620 // Loop over the shared boundaries with other processors and get:
18621 // 1) The processors defining the shared boundary
18622 // 2) The shared boundary id
18623 // 3) The index of the node on the shared boundary
18624 Vector<unsigned> other_processor_1;
18625 Vector<unsigned> other_processor_2;
18626 Vector<unsigned> shd_bnd_ids;
18627 Vector<unsigned> indexes;
18628 // Loop over the processors again
18629 for (unsigned jproc = 0; jproc < nproc; jproc++)
18630 {
18631 // Do not search with the iproc processor, that was done before
18632 // above
18633 if (jproc != iproc)
18634 {
18635 // Get the number of shared boundaries with the jproc
18636 // processor
18637 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
18638 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
18639 {
18640 // Get the boundary id
18641 const unsigned j_shd_bnd =
18642 this->shared_boundaries_ids(my_rank, jproc, bb);
18643 // Is the node part of this boundary?
18644 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
18645 {
18646 // Include the first processor
18647 other_processor_1.push_back(my_rank);
18648 // Include the second processor
18649 other_processor_2.push_back(jproc);
18650 // Include the shared boundary id
18651 shd_bnd_ids.push_back(j_shd_bnd);
18652 // Increase the counter for found shared boundaries with
18653 // other processors
18654 counter_shd_bnd_with_other_procs_have_node++;
18655 }
18656
18657 } // for (bb < nshared_bnd)
18658
18659 } // if (jproc != iproc)
18660
18661 } // for (jproc < nproc)
18662
18663 // Get the indexes of the node on all the shared boundaries where
18664 // it was found
18665 const unsigned n_other_processors = other_processor_1.size();
18666 // Loop over the processors where the node was found
18667 for (unsigned i = 0; i < n_other_processors; i++)
18668 {
18669 // Get the shared boundary id
18670 unsigned shd_bnd_id = shd_bnd_ids[i];
18671 // Get the number of nodes on that shared boundary
18672 const unsigned n_nodes_on_shd_bnd =
18673 nsorted_shared_boundary_node(shd_bnd_id);
18674
18675#ifdef PARANOID
18676 bool found_index_node_on_shared_boundary = false;
18677#endif
18678 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
18679 {
18680 // Get the i-th shared boundary node
18681 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
18682 // Is the same node?
18683 if (shared_node_pt == nod_pt)
18684 {
18685 // DEBP(i_node);
18686 // DEBP(nod_pt->x(0));
18687 // DEBP(nod_pt->x(1));
18688 // Include the index of the node
18689 indexes.push_back(i);
18690#ifdef PARANOID
18691 // Mark as found the node
18692 found_index_node_on_shared_boundary = true;
18693#endif
18694 break;
18695 } // if (shared_node_pt == nod_pt)
18696
18697 } // for (i < n_nodes_on_shd_bnd)
18698
18699#ifdef PARANOID
18700 if (!found_index_node_on_shared_boundary)
18701 {
18702 std::ostringstream error_message;
18703 error_message << "The index of the node on boundary (" << shd_bnd_id
18704 << "), shared by other processors\nwas not found.\n"
18705 << "The node coordinates are (" << nod_pt->x(0) << ","
18706 << nod_pt->x(1) << ").\n";
18707 throw OomphLibError(
18708 error_message.str(),
18709 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18710 OOMPH_EXCEPTION_LOCATION);
18711 }
18712#endif
18713 } // for (i < n_other_processors)
18714
18715 // Now send the info. but first check that the number of found
18716 // nodes be the same that the previously found shared boundaries
18717 // with the node
18718#ifdef PARANOID
18719 if (counter_shd_bnd_with_other_procs_have_node !=
18720 nshared_boundaries_with_other_processors_have_node)
18721 {
18722 std::ostringstream error_message;
18723 error_message << "The number of shared boundaries where the node is on "
18724 << "is different:\n"
18725 << "nshared_boundaries_with_other_processors_have_node: ("
18726 << nshared_boundaries_with_other_processors_have_node
18727 << ")\n"
18728 << "counter_shd_bnd_with_other_procs_have_node: ("
18729 << counter_shd_bnd_with_other_procs_have_node << ")\n";
18730 throw OomphLibError(
18731 error_message.str(),
18732 "RefineableTriangleMesh::get_required_nodal_information_helper()",
18733 OOMPH_EXCEPTION_LOCATION);
18734 } // if (counter_shd_bnd_with_other_procs_have_node !=
18735 // nshared_boundaries_with_other_processors_have_node)
18736#endif
18737
18738 // Loop over the info. to send it
18739 for (unsigned i = 0; i < n_other_processors; i++)
18740 {
18741 Flat_packed_unsigneds.push_back(other_processor_1[i]);
18742#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18743 std::stringstream junk1;
18744 junk1 << "Processor where the other shared boundary "
18745 << "has the node: " << other_processor_1[i];
18746 Flat_packed_unsigneds_string.push_back(junk1.str());
18747#endif
18748
18749 Flat_packed_unsigneds.push_back(other_processor_2[i]);
18750#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18751 std::stringstream junk2;
18752 junk2 << "Processor where the other shared boundary "
18753 << "has the node: " << other_processor_2[i];
18754 Flat_packed_unsigneds_string.push_back(junk2.str());
18755#endif
18756
18757 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
18758#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18759 std::stringstream junk3;
18760 junk3 << "Other shared boundary id where the node is on"
18761 << boundaries[i];
18762 Flat_packed_unsigneds_string.push_back(junk3.str());
18763#endif
18764
18765 Flat_packed_unsigneds.push_back(indexes[i]);
18766#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18767 std::stringstream junk4;
18768 junk4 << "Node index on other shared boundary " << boundaries[i]
18769 << " is " << indexes[i];
18770 Flat_packed_unsigneds_string.push_back(junk4.str());
18771#endif
18772
18773 } // for (i < n_other_processors)
18774
18775 } // if (node_on_shared_boundary_with_other_processors)
18776 else
18777 {
18778 Flat_packed_unsigneds.push_back(0);
18779#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18781 "Node is on any shared boundary with other processors");
18782#endif
18783 } // else if (node_on_shared_boundary_with_other_processors)
18784
18785 // Now check if it is required to send the info. of the node. If the
18786 // node is not on a shared boundary with the iproc processor then we
18787 // need to send the info.
18788
18789 if (!node_on_shared_boundary)
18790 {
18791 // Send all the info. to create it
18792
18793 // Is the Node algebraic? If so, send its ref values and
18794 // an indication of its geometric objects if they are stored
18795 // in the algebraic mesh
18796 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
18797 if (alg_nod_pt != 0)
18798 {
18799 // The external mesh should be algebraic
18800 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
18801
18802 // Get default node update function ID
18803 unsigned update_id = alg_nod_pt->node_update_fct_id();
18804 Flat_packed_unsigneds.push_back(update_id);
18805#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18806 Flat_packed_unsigneds_string.push_back("Alg Node update id");
18807#endif
18808
18809 // Get reference values at default...
18810 unsigned n_ref_val = alg_nod_pt->nref_value();
18811 Flat_packed_unsigneds.push_back(n_ref_val);
18812#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18813 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
18814#endif
18815 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
18816 {
18817 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
18818 }
18819
18820 // Access geometric objects at default...
18821 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
18822 Flat_packed_unsigneds.push_back(n_geom_obj);
18823#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18824 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
18825#endif
18826 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
18827 {
18828 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
18829
18830 // Check this against the stored geometric objects in mesh
18831 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
18832
18833 // Default found index to zero
18834 unsigned found_geom_object = 0;
18835 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
18836 {
18837 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
18838 {
18839 found_geom_object = i_list;
18840 }
18841 }
18842 Flat_packed_unsigneds.push_back(found_geom_object);
18843#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18844 Flat_packed_unsigneds_string.push_back("Found geom object");
18845#endif
18846 }
18847 } // (if alg_nod_pt!=0)
18848
18849 // Is it a SolidNode?
18850 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
18851 if (solid_nod_pt != 0)
18852 {
18853 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
18854 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
18855 {
18856 for (unsigned t = 0; t < n_prev; t++)
18857 {
18858 Flat_packed_doubles.push_back(
18859 solid_nod_pt->variable_position_pt()->value(t, i_val));
18860 }
18861 }
18862
18863 Vector<double> values_solid_node;
18864 solid_nod_pt->add_values_to_vector(values_solid_node);
18865 const unsigned nvalues_solid_node = values_solid_node.size();
18866 Flat_packed_unsigneds.push_back(nvalues_solid_node);
18867#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18868 std::stringstream junk;
18869 junk << "Number of values solid node: " << nvalues_solid_node;
18870 Flat_packed_unsigneds_string.push_back(junk.str());
18871#endif
18872 for (unsigned i = 0; i < nvalues_solid_node; i++)
18873 {
18874 Flat_packed_doubles.push_back(values_solid_node[i]);
18875 }
18876 }
18877
18878 // Finally copy info required for all node types
18879 for (unsigned i_val = 0; i_val < n_val; i_val++)
18880 {
18881 for (unsigned t = 0; t < n_prev; t++)
18882 {
18883 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
18884 }
18885 }
18886
18887 // Now do positions
18888 for (unsigned idim = 0; idim < n_dim; idim++)
18889 {
18890 for (unsigned t = 0; t < n_prev; t++)
18891 {
18892 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
18893 }
18894 }
18895
18896 } // if (!node_on_shared_boundary)
18897 }
18898
18899 //==========start of add_haloed_node_helper===============================
18900 /// Helper to add external haloed node that is not a master
18901 //========================================================================
18902 template<class ELEMENT>
18904 Node* nod_pt)
18905 {
18906 // Attempt to add this node as a haloed node
18907 const unsigned n_haloed_nod = this->nhaloed_node(iproc);
18908 const unsigned haloed_node_index =
18909 this->try_to_add_haloed_node_pt(iproc, nod_pt);
18910
18911 // If it was added then the new index should match the size of the storage
18912 if (haloed_node_index == n_haloed_nod)
18913 {
18914 Flat_packed_unsigneds.push_back(1);
18915
18916#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18917 std::stringstream junk;
18918 junk << "Node needs to be constructed [size="
18919 << Flat_packed_unsigneds.size() << "]; last entry: "
18921 Flat_packed_unsigneds_string.push_back(junk.str());
18922#endif
18923
18924 // This helper function gets all the required information for the
18925 // specified node and stores it into MPI-sendable information
18926 // so that a halo copy can be made on the receiving process
18928 }
18929 else // It was already added
18930 {
18931 Flat_packed_unsigneds.push_back(0);
18932#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18933 std::stringstream junk;
18934 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
18935 << "]; last entry: "
18937
18938 Flat_packed_unsigneds_string.push_back(junk.str());
18939#endif
18940
18941 // This node is already a haloed node, so tell
18942 // the other process its index in the equivalent halo storage
18943 Flat_packed_unsigneds.push_back(haloed_node_index);
18944#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
18945 Flat_packed_unsigneds_string.push_back("haloed node index");
18946#endif
18947 }
18948 }
18949
18950 //================= send_and_receive_haloed_info =======================
18951 /// Send the information of the elements that will be created on the other
18952 /// processor
18953 //======================================================================
18954 template<class ELEMENT>
18956 int& send_proc, int& recv_proc)
18957 {
18958 // Get the communicator of the mesh
18959 OomphCommunicator* comm_pt = this->communicator_pt();
18960
18961 // Set MPI info
18962 MPI_Status status;
18963 MPI_Request request;
18964
18965 // Prepare vectors to receive information
18966 Vector<double> received_double_values;
18967 Vector<unsigned> received_unsigned_values;
18968
18969 // Send the double values associated with halo(ed) elements and nodes
18970 //-------------------------------------------------------------------
18971 unsigned send_count_double_values = Flat_packed_doubles.size();
18972 MPI_Isend(&send_count_double_values,
18973 1,
18974 MPI_UNSIGNED,
18975 send_proc,
18976 1,
18977 comm_pt->mpi_comm(),
18978 &request);
18979
18980 int receive_count_double_values = 0;
18981 MPI_Recv(&receive_count_double_values,
18982 1,
18983 MPI_INT,
18984 recv_proc,
18985 1,
18986 comm_pt->mpi_comm(),
18987 &status);
18988 MPI_Wait(&request, MPI_STATUS_IGNORE);
18989
18990 if (send_count_double_values != 0)
18991 {
18992 MPI_Isend(&Flat_packed_doubles[0],
18993 send_count_double_values,
18994 MPI_DOUBLE,
18995 send_proc,
18996 2,
18997 comm_pt->mpi_comm(),
18998 &request);
18999 }
19000 if (receive_count_double_values != 0)
19001 {
19002 received_double_values.resize(receive_count_double_values);
19003 MPI_Recv(&received_double_values[0],
19004 receive_count_double_values,
19005 MPI_DOUBLE,
19006 recv_proc,
19007 2,
19008 comm_pt->mpi_comm(),
19009 &status);
19010 }
19011 if (send_count_double_values != 0)
19012 {
19013 MPI_Wait(&request, MPI_STATUS_IGNORE);
19014 }
19015
19016 // Now send unsigned values associated with halo(ed) elements and nodes
19017 //---------------------------------------------------------------------
19018 unsigned send_count_unsigned_values = Flat_packed_unsigneds.size();
19019#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19020 unsigned send_count_unsigned_string = Flat_packed_unsigneds_string.size();
19021#ifdef PARANOID
19022 if (send_count_unsigned_string != send_count_unsigned_values)
19023 {
19024 std::ostringstream error_message;
19025 error_message << "The number of unsigned values to send to processor ("
19026 << send_proc
19027 << ") is different from the\nnumber of annotated strings "
19028 << "for the communication\n\n";
19029 throw OomphLibError(
19030 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
19031 }
19032#endif // #ifdef PARANOID
19033#endif
19034 MPI_Isend(&send_count_unsigned_values,
19035 1,
19036 MPI_UNSIGNED,
19037 send_proc,
19038 14,
19039 comm_pt->mpi_comm(),
19040 &request);
19041
19042 int receive_count_unsigned_values = 0;
19043 MPI_Recv(&receive_count_unsigned_values,
19044 1,
19045 MPI_INT,
19046 recv_proc,
19047 14,
19048 comm_pt->mpi_comm(),
19049 &status);
19050
19051 MPI_Wait(&request, MPI_STATUS_IGNORE);
19052
19053 if (send_count_unsigned_values != 0)
19054 {
19055 MPI_Isend(&Flat_packed_unsigneds[0],
19056 send_count_unsigned_values,
19057 MPI_UNSIGNED,
19058 send_proc,
19059 15,
19060 comm_pt->mpi_comm(),
19061 &request);
19062#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19063 for (unsigned i = 0; i < send_count_unsigned_values; i++)
19064 {
19065 oomph_info << "Sent:" << i << " to orig_proc:" << send_proc << " "
19067 << Flat_packed_unsigneds[i] << std::endl;
19068 }
19069#endif
19070 }
19071 if (receive_count_unsigned_values != 0)
19072 {
19073 received_unsigned_values.resize(receive_count_unsigned_values);
19074 MPI_Recv(&received_unsigned_values[0],
19075 receive_count_unsigned_values,
19076 MPI_UNSIGNED,
19077 recv_proc,
19078 15,
19079 comm_pt->mpi_comm(),
19080 &status);
19081 }
19082
19083 if (send_count_unsigned_values != 0)
19084 {
19085 MPI_Wait(&request, MPI_STATUS_IGNORE);
19086 }
19087
19088 // Copy across into original containers -- these can now
19089 //------------------------------------------------------
19090 // be processed by create_external_halo_elements() to generate
19091 //------------------------------------------------------------
19092 // external halo elements
19093 //------------------------
19094 Flat_packed_doubles.resize(receive_count_double_values);
19095 for (int ii = 0; ii < receive_count_double_values; ii++)
19096 {
19097 Flat_packed_doubles[ii] = received_double_values[ii];
19098 }
19099 Flat_packed_unsigneds.resize(receive_count_unsigned_values);
19100 for (int ii = 0; ii < receive_count_unsigned_values; ii++)
19101 {
19102 Flat_packed_unsigneds[ii] = received_unsigned_values[ii];
19103 }
19104 }
19105
19106 //=====================================================================
19107 /// Creates (halo) element on the loop process based on the
19108 /// information received from each processor
19109 //=====================================================================
19110 template<class ELEMENT>
19112 unsigned& iproc,
19113 Vector<Node*>& new_nodes_on_domain,
19114 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19115 other_proc_shd_bnd_node_pt,
19116 Vector<Vector<Vector<unsigned>>>& global_node_names,
19117 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19118 Vector<Node*>& global_shared_node_pt)
19119 {
19120#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19122 << " Bool: New element needs to be constructed "
19124 << std::endl;
19125#endif
19126
19128 {
19129 // Create a new element from the communicated values
19130 // and coords from the process that located zeta
19131 GeneralisedElement* new_el_pt = new ELEMENT;
19132
19133 // Add the element, it is a new element in the mesh
19134 this->add_element_pt(new_el_pt);
19135
19136 // Add halo element to this mesh
19137 this->add_root_halo_element_pt(iproc, new_el_pt);
19138
19139 // Cast to the FE pointer
19140 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
19141
19142 // Check if new element is associated to any boundary
19143 this->add_halo_element_helper(iproc, f_el_pt);
19144
19145 // Now we add nodes to the new element
19146 unsigned n_node = f_el_pt->nnode();
19147
19148 for (unsigned j = 0; j < n_node; j++)
19149 {
19150 Node* new_nod_pt = 0;
19151
19152 // Call the add halo node helper function
19153 add_halo_node_helper(new_nod_pt,
19154 new_nodes_on_domain,
19155 other_proc_shd_bnd_node_pt,
19156 iproc,
19157 j,
19158 f_el_pt,
19159 global_node_names,
19160 node_name_to_global_index,
19161 global_shared_node_pt);
19162
19163 } // for (j<n_nod)
19164 }
19165 else // the element already exists as halo
19166 {
19167#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19169 << " Index of existing halo element "
19171 << std::endl;
19172#endif
19173 // The index itself is in Flat_packed_unsigneds[...]
19174 unsigned halo_ele_index =
19176
19177 // Use this index to get the element
19178 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(
19179 this->root_halo_element_pt(iproc, halo_ele_index));
19180
19181 // If it's not a finite element die
19182 if (f_el_pt == 0)
19183 {
19184 throw OomphLibError("Halo element is not a FiniteElement\n",
19185 OOMPH_CURRENT_FUNCTION,
19186 OOMPH_EXCEPTION_LOCATION);
19187 }
19188
19189 } // else the element already exists as halo
19190 }
19191
19192 //========start of add_halo_element_helper==============================
19193 /// Helper function to create (halo) elements on the loop
19194 /// process based on the info received in send_and_received_located_info
19195 /// This function is in charge of verify if the element is associated to
19196 /// a boundary
19197 //======================================================================
19198 template<class ELEMENT>
19200 unsigned& iproc, FiniteElement* ele_pt)
19201 {
19202#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19204 << " Bool: Element is associated to an original boundary "
19206 << std::endl;
19207#endif
19208
19210 {
19211#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19213 << " How many boundaries are associated with the element "
19215 << std::endl;
19216#endif
19217 const unsigned nassociated_boundaries =
19219
19220 for (unsigned b = 0; b < nassociated_boundaries; b++)
19221 {
19222#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19224 << " Boundary associated to the element "
19226 << std::endl;
19227#endif
19228 const unsigned bnd =
19230
19231#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19233 << " Face index of the element "
19235 << std::endl;
19236#endif
19237 const unsigned face_index =
19239
19240 // Associate the element with the boundary and establish as many
19241 // face indexes it has
19242 this->Boundary_element_pt[bnd].push_back(ele_pt);
19243 this->Face_index_at_boundary[bnd].push_back(face_index);
19244
19245 } // (b < nassociated_boundaries)
19246
19247 // Here read the info. regarding the boundary-region of the element
19248#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19250 << " Bool: Element is associated to a boundary-region "
19252 << std::endl;
19253#endif
19254
19256 {
19257#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19260 << " How many boundaries-regions are associated with the element "
19262 << std::endl;
19263#endif
19264 const unsigned nassociated_boundaries_and_regions =
19266
19267 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
19268 {
19269#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19271 << " Boundary associated to the element "
19273 << std::endl;
19274#endif
19275 const unsigned bnd =
19277
19278#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19280 << " Region associated to the element "
19282 << std::endl;
19283#endif
19284 const unsigned region =
19286
19287#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19289 << " Face index of the element in boundary-region "
19291 << std::endl;
19292#endif
19293 const unsigned face_index =
19295
19296 // Associate the element with the boundary-regions and establish
19297 // as many face indexes it has
19298 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
19299 this->Face_index_region_at_boundary[bnd][region].push_back(
19300 face_index);
19301
19302 } // for (br < nassociated_boundaries_and_regions)
19303
19304 } // Is the element associated with a boundary-region?
19305 }
19306
19307 // Now check if the element is associated to a shared boundary
19308#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19310 << " Bool: Element is associated to a shared boundary "
19312 << std::endl;
19313#endif
19315 {
19316#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19319 << " How many shared boundaries are associated with the element "
19321 << std::endl;
19322#endif
19323 const unsigned nassociated_shared_boundaries =
19325
19326 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
19327 {
19328#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19330 << " Shared boundary associated to the element "
19332 << std::endl;
19333#endif
19334 const unsigned bnd =
19336
19337#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19340 << " Face index of the element associated to the shared boundary "
19342 << std::endl;
19343#endif
19344
19345 const unsigned face_index =
19347
19348 this->add_shared_boundary_element(bnd, ele_pt);
19349 this->add_face_index_at_shared_boundary(bnd, face_index);
19350
19351 } // (b < nassociated_shared_boundaries)
19352
19353 } // The element is associted with a shared boundary
19354 }
19355
19356 //========start of add_halo_node_helper==========================
19357 /// Helper function to add halo node
19358 //===============================================================
19359 template<class ELEMENT>
19361 Node*& new_nod_pt,
19362 Vector<Node*>& new_nodes_on_domain,
19363 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19364 other_proc_shd_bnd_node_pt,
19365 unsigned& iproc,
19366 unsigned& node_index,
19367 FiniteElement* const& new_el_pt,
19368 Vector<Vector<Vector<unsigned>>>& global_node_names,
19369 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19370 Vector<Node*>& global_shared_node_pt)
19371 {
19372 // Given the node, received information about them from process
19373 // iproc, construct them on the current process
19374#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19376 << " Bool: New node needs to be constructed "
19378 << std::endl;
19379#endif
19381 {
19382 // Construct a new node based upon sent information, or copy a node
19383 // from one of the shared boundaries
19384 construct_new_halo_node_helper(new_nod_pt,
19385 new_nodes_on_domain,
19386 other_proc_shd_bnd_node_pt,
19387 iproc,
19388 node_index,
19389 new_el_pt,
19390 global_node_names,
19391 node_name_to_global_index,
19392 global_shared_node_pt);
19393 }
19394 else
19395 {
19396#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19398 << " Index of existing halo node "
19400 << std::endl;
19401#endif
19402
19403 // Copy node from received location
19404 new_nod_pt = new_nodes_on_domain
19406
19407 new_el_pt->node_pt(node_index) = new_nod_pt;
19408 }
19409 }
19410
19411 //========start of construct_new_halo_node_helper=================
19412 // Helper function which constructs a new external halo node (on new element)
19413 // with the required information sent from the haloed process
19414 //========================================================================
19415 template<class ELEMENT>
19417 Node*& new_nod_pt,
19418 Vector<Node*>& new_nodes_on_domain,
19419 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
19420 other_proc_shd_bnd_node_pt,
19421 unsigned& iproc,
19422 unsigned& node_index,
19423 FiniteElement* const& new_el_pt,
19424 Vector<Vector<Vector<unsigned>>>& global_node_names,
19425 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
19426 Vector<Node*>& global_shared_node_pt)
19427 {
19428 // The first entry indicates the number of values at this new Node
19429 //(which may be different across the same element e.g. Lagrange multipliers)
19430#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19432 << " Number of values of external halo node "
19434 << std::endl;
19435#endif
19437
19438 // Null TimeStepper for now
19439 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
19440 // Default number of previous values to 1
19441 unsigned n_prev = time_stepper_pt->ntstorage();
19442
19443 // ------------------------------------------------------
19444 // Check if the node is on an original boundary
19445#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19447 << " Is the node on an original boundary "
19449 << std::endl;
19450#endif
19451
19452 // Flag to indicate if the node is on original boundaries
19453 const unsigned node_on_original_boundaries =
19455
19456 // Store the original boundaries where the node is on
19457 Vector<unsigned> original_boundaries_node_is_on;
19458 // Store the zeta coordinates of the node on the original boundaries
19459 Vector<double> zeta_coordinates;
19460 // Store the number of original boundaries the node is on
19461 unsigned n_original_boundaries_node_is_on = 0;
19462
19463 if (node_on_original_boundaries == 2)
19464 {
19465 // How many original boundaries does the node live on?
19466#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19468 << " Number of boundaries the node is on: "
19470 << std::endl;
19471#endif
19472 n_original_boundaries_node_is_on =
19474
19475 // Resize the containers
19476 original_boundaries_node_is_on.resize(n_original_boundaries_node_is_on);
19477 zeta_coordinates.resize(n_original_boundaries_node_is_on);
19478
19479 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19480 {
19481 // Boundary number
19482#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19484 << " Node is on boundary "
19486 << std::endl;
19487#endif
19488 original_boundaries_node_is_on[i] =
19490 zeta_coordinates[i] =
19492 }
19493
19494 } // if (node_on_original_boundaries==2)
19495#ifdef PARANOID
19496 else
19497 {
19498 if (node_on_original_boundaries != 0)
19499 {
19500 std::ostringstream error_message;
19501 error_message
19502 << "The current node is not on an original boundary, this should\n"
19503 << "be indicated by a zero flag. However, the read value for\n"
19504 << "that flag is (" << node_on_original_boundaries << ").\n\n";
19505 throw OomphLibError(
19506 error_message.str(),
19507 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19508 OOMPH_EXCEPTION_LOCATION);
19509 } // if (node_on_original_boundaries != 0)
19510 }
19511#endif
19512
19513 // --------------------------------------------------------------
19514 // Check if the node was on a shared boundary with the iproc
19515 // processor
19516#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19518 << " Is node on shared boundary? "
19520 << std::endl;
19521#endif
19522 const unsigned is_node_on_shared_boundary =
19524 if (is_node_on_shared_boundary == 1)
19525 {
19526 // How many shared boundaries does the node live on?
19527#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19529 << " Number of boundaries the node is on: "
19531 << std::endl;
19532#endif
19533 const unsigned n_shd_bnd_node_is_on =
19535 Vector<unsigned> shd_bnds_node_is_on(n_shd_bnd_node_is_on);
19536 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
19537 {
19538 // Shared boundary number
19539#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19541 << " Node is on boundary "
19543 << std::endl;
19544#endif
19545 shd_bnds_node_is_on[i] =
19547 }
19548
19549 // Get the index of the node on the shared boundary
19550#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19552 << " Index of node on boundary "
19554 << std::endl;
19555#endif
19556 // Get the node index of the node on the shared boundary
19557 unsigned node_index_on_shared_boundary =
19559
19560 // Get the pointer to the node with the received info.
19561 new_nod_pt = this->sorted_shared_boundary_node_pt(
19562 shd_bnds_node_is_on[0], node_index_on_shared_boundary);
19563
19564 } // if (is_node_on_shared_boundary == 1)
19565#ifdef PARANOID
19566 else
19567 {
19568 if (is_node_on_shared_boundary != 0)
19569 {
19570 std::ostringstream error_message;
19571 error_message
19572 << "The current node is not on a shared boundary, this should\n"
19573 << "be indicated by a zero flag. However, the read value for\n"
19574 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
19575 throw OomphLibError(
19576 error_message.str(),
19577 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19578 OOMPH_EXCEPTION_LOCATION);
19579 } // if (node_on_shared_boundary != 0)
19580 }
19581#endif
19582
19583 // ------------------------------------------------------------
19584 // Is the node on a shared boundary with other processor?
19585#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19587 << " Is the node on shared boundaries with other processors "
19589 << std::endl;
19590#endif
19591
19592 // Is the node in shared boundaries no associated with the
19593 // receiver processor
19594 const unsigned is_the_node_in_shared_boundaries_with_other_processors =
19596
19597 // The containers where to store the info.
19598 Vector<unsigned> other_processor_1;
19599 Vector<unsigned> other_processor_2;
19600 Vector<unsigned> other_shared_boundaries;
19601 Vector<unsigned> other_indexes;
19602
19603 // How many shared bounaries with other processors the node lives on
19604 unsigned n_shd_bnd_with_other_procs_have_node = 0;
19605
19606 // Is the node on shared boundaries with other processors
19607 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19608 {
19609#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19611 << " In how many shared boundaries with other "
19612 << "processors is the node "
19614 << std::endl;
19615#endif
19616
19617 // How many nodes on other shared boundaries were found
19618 n_shd_bnd_with_other_procs_have_node =
19620
19621 // Resize the containers
19622 other_processor_1.resize(n_shd_bnd_with_other_procs_have_node);
19623 other_processor_2.resize(n_shd_bnd_with_other_procs_have_node);
19624 other_shared_boundaries.resize(n_shd_bnd_with_other_procs_have_node);
19625 other_indexes.resize(n_shd_bnd_with_other_procs_have_node);
19626
19627 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19628 {
19629#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19631 << " Processor where the other shared boundary"
19632 << "has the node"
19634 << std::endl;
19635#endif
19636 // Read the other processor 1
19637 other_processor_1[i] =
19639
19640#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19642 << " Processor where the other shared boundary"
19643 << "has the node"
19645 << std::endl;
19646#endif
19647 // Read the other processor 2
19648 other_processor_2[i] =
19650
19651#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19653 << " Other shared boundary id where the node is on: "
19655 << std::endl;
19656#endif
19657
19658 // Read the other shared boundary id
19659 other_shared_boundaries[i] =
19661
19662#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
19664 << " Node index on the other shared boundary "
19666 << std::endl;
19667#endif
19668
19669 // Read the node index on the other shared boundary
19670 other_indexes[i] =
19672
19673 } // for (i < n_shd_bnd_with_other_procs_have_node)
19674
19675 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19676#ifdef PARANOID
19677 else
19678 {
19679 if (is_the_node_in_shared_boundaries_with_other_processors != 0)
19680 {
19681 std::ostringstream error_message;
19682 error_message
19683 << "The current node is not on a shared boundary with\n"
19684 << "other processors, this should be indicated by a zero flag.\n"
19685 << "However, the read value for that flag is ("
19686 << is_the_node_in_shared_boundaries_with_other_processors << ").\n\n";
19687 throw OomphLibError(
19688 error_message.str(),
19689 "RefineableTriangleMesh::construct_new_halo_node_helper()",
19690 OOMPH_EXCEPTION_LOCATION);
19691 }
19692 }
19693#endif
19694
19695 // Now we have all the info. to decide whether the node should be
19696 // created or not
19697
19698 // First check if the node is a shared boundary node
19699 if (is_node_on_shared_boundary == 1)
19700 {
19701 // We already have the node, we do not need to create it
19702
19703 // Only check if we need to add boundary info. to the node
19704 if (node_on_original_boundaries == 2)
19705 {
19706 // The node is a boundary node, add the boundary info. before
19707 // adding it to the domain
19708
19709 // Associate the node to the given boundaries
19710 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19711 {
19712 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19713 // Establish the boundary coordinates for the node
19714 Vector<double> zeta(1);
19715 zeta[0] = zeta_coordinates[i];
19716 new_nod_pt->set_coordinates_on_boundary(
19717 original_boundaries_node_is_on[i], zeta);
19718 }
19719
19720 } // if (node_on_original_boundaries==2)
19721
19722 // Add the node to the domain
19723 new_nodes_on_domain.push_back(new_nod_pt);
19724
19725 // Add the node to the element
19726 new_el_pt->node_pt(node_index) = new_nod_pt;
19727
19728 } // if (is_node_on_shared_boundary == 1)
19729
19730 // Now check if the node is on a shared boundary with another
19731 // processor, if that is the case try to find the node that may have
19732 // been already sent by the other processors
19733
19734 // This flags indicates if the node was found, and then decide if it
19735 // is required to create the node
19736 bool found_node_in_other_shared_boundaries = false;
19737 // Flag to indicate whether the node should be created as a boundary
19738 // node or not. If the node lies on a shared boundary with other
19739 // processor the we create it as a boundary node. The processor from
19740 // which we are receiving info. (iproc) may not know that the node
19741 // lies on an original boundary. If the node lies on an original
19742 // boundary then its info. will be sent by another processor, then
19743 // we can set its boundary info. since the node was constructed as a
19744 // boundary node
19745 bool build_node_as_boundary_node = false;
19746
19747 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19748 {
19749 // Build the node as a boundary node
19750 build_node_as_boundary_node = true;
19751
19752 // Try to get the node pointer in case that the node has been
19753 // already sent by the other processors
19754
19755 // Get the number of initial shared boundaries to correct the
19756 // index of the shared boundary
19757 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
19758
19759 // Add the found nodes in the container
19760 Vector<Node*> found_node_pt;
19761
19762 // Now try to find the node in any of the other shared boundaries
19763 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
19764 {
19765 // We always check with the lower processor number. The
19766 // info. is only stored in one direction. More importantly,
19767 // this is done with the hope that the info. has been already
19768 // received from the other processor given that its info. was
19769 // processed before the current processor (iproc). NOTE that
19770 // it is not always the case that this info. has been received
19771 // from the other processors since it may have not require to
19772 // send the elements (and nodes) on the shared boundary with
19773 // the current processor (iproc).
19774 unsigned oproc1 = other_processor_1[i];
19775 unsigned oproc2 = other_processor_2[i];
19776 if (other_processor_1[i] > other_processor_2[i])
19777 {
19778 oproc1 = other_processor_2[i];
19779 oproc2 = other_processor_1[i];
19780 } // if (other_processor_1[i] > other_processor_2[i])
19781
19782 // Re-compute the shared boundary id between the other
19783 // processors
19784 const unsigned shd_bnd_id =
19785 other_shared_boundaries[i] - initial_shd_bnd_id;
19786
19787 // Read the index
19788 const unsigned index = other_indexes[i];
19789
19790 // Check if there are nodes received from the other processor
19791 // and with the given shared boundary
19792 const unsigned n_nodes_on_other_processor =
19793 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].size();
19794
19795 if (n_nodes_on_other_processor > 0)
19796 {
19797 // Check if we can find the index of the node in that
19798 // other processor and shared boundary id
19799 std::map<unsigned, Node*>::iterator it =
19800 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].find(index);
19801
19802 // If the index exist then get the node pointer
19803 if (it !=
19804 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
19805 {
19806 // Mark the node as found
19807 found_node_in_other_shared_boundaries = true;
19808 // Get the node pointer
19809 Node* tmp_node_pt = (*it).second;
19810
19811 // Push back the node pointer
19812 found_node_pt.push_back(tmp_node_pt);
19813
19814 } // if (it!=
19815 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
19816
19817 } // if (n_nodes_on_other_processor > 0)
19818
19819 } // for (i < n_shd_bnd_with_other_procs_have_node)
19820
19821 // If the node was found, then all their instances should be the
19822 // same but better check
19823 if (found_node_in_other_shared_boundaries)
19824 {
19825#ifdef PARANOID
19826 const unsigned n_times_node_found = found_node_pt.size();
19827 for (unsigned j = 1; j < n_times_node_found; j++)
19828 {
19829 if (found_node_pt[j - 1] != found_node_pt[j])
19830 {
19831 std::ostringstream error_message;
19832 error_message
19833 << "The instances of the node that was found on\n"
19834 << "shared boundaries with other processors (but not\n"
19835 << "on shared boundaries with this processor) are not\n"
19836 << "the same.\n"
19837 << "These are the coordinates of the instances of the\n"
19838 << "nodes:\n"
19839 << "(" << found_node_pt[j - 1]->x(0) << ", "
19840 << found_node_pt[j - 1]->x(1) << ")\n"
19841 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
19842 << ")\n"
19843 << "Dont be surprised if they are the same since the "
19844 << "node is\nrepeated.\n";
19845 throw OomphLibError(error_message.str(),
19846 OOMPH_CURRENT_FUNCTION,
19847 OOMPH_EXCEPTION_LOCATION);
19848
19849 } // if (found_node_pt[j-1] != found_node_pt[j])
19850
19851 } // for (j < ntimes_node_found)
19852#endif // #ifdef PARANOID
19853
19854 // Check if the node is a shared boundary node from the
19855 // current processor and the iproc processor, if that is the
19856 // case, and the node is also on a shared boundary with other
19857 // processor, then the pointer should be the same!!!
19858 if (is_node_on_shared_boundary == 1)
19859 {
19860 // const unsigned n_times_node_found = found_node_pt.size();
19861 // The pointer to the node is already assigned, it was
19862 // assigned when the node was found to be on a shared
19863 // boundary with the sending processor (iproc). Check that
19864 // any previous instances of the node have been copied
19865 // from the shared boundary, if that is not the case then
19866 // there is a problem
19867 if (found_node_pt[0] != new_nod_pt)
19868 {
19869 std::ostringstream error_message;
19870 error_message
19871 << "The pointer of the node that was found to be on a\n"
19872 << "shared boundary with other processor(s) and the pointer\n"
19873 << "of the node on shared boundary with the receiver\n"
19874 << "processor (iproc) are not the same. This means we have a\n"
19875 << "repeated node)\n"
19876 << "The coordinates for the nodes are:\n"
19877 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
19878 << ")\n"
19879 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
19880 << "Dont be surprised if they are the same since the "
19881 << "node is\nrepeated.\n";
19882 throw OomphLibError(error_message.str(),
19883 OOMPH_CURRENT_FUNCTION,
19884 OOMPH_EXCEPTION_LOCATION);
19885
19886 } // if (found_node_pt[i] != new_nod_pt)
19887
19888 } // if (is_node_on_shared_boundary == 1)
19889 else
19890 {
19891 // Take the first instance of the node in case that it was
19892 // found and is not on a shared boundary with the iproc
19893 // processor (the processor from which we are receiving
19894 // the info.)
19895 new_nod_pt = found_node_pt[0];
19896 }
19897
19898 } // if (found_node_in_other_shared_boundaries)
19899
19900 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
19901
19902 // -----------------------------------------------------------------
19903 // Create the node or read the received info if the node is not on a
19904 // shared boundary with the iproc processor
19905 if (is_node_on_shared_boundary != 1)
19906 {
19907 // If the node is on a shared boundary with other processor we
19908 // need to read all the info. since the processor that sent the
19909 // info. did not know that the node is part of another shared
19910 // boundary
19911
19912 // If the node is not on a shared boundary (with any processor),
19913 // or if this is the first time that the info. of the node is
19914 // received from any of the processors with which it has a shared
19915 // boundary, then we create the node
19916
19917 // Is the node a boundary node or should it be build as a boundary
19918 // node because it is on a shared boundary with other processors
19919 if (node_on_original_boundaries == 2 || build_node_as_boundary_node)
19920 {
19921 // Check if necessary to create the node, or if it has been
19922 // already found in shared boundaries with other processors
19923 if (!found_node_in_other_shared_boundaries)
19924 {
19925 // Construct a boundary node
19926 if (time_stepper_pt != 0)
19927 {
19928 new_nod_pt =
19929 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
19930 }
19931 else
19932 {
19933 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
19934 }
19935
19936 } // if (!found_node_in_other_shared_boundaries)
19937 else
19938 {
19939 // If the node was found then assign the node to the element
19940 new_el_pt->node_pt(node_index) = new_nod_pt;
19941
19942 } // else if (!found_node_in_other_shared_boundaries)
19943
19944 // Associate the node to the given boundaries
19945 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
19946 {
19947 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
19948 // Establish the boundary coordinates for the node
19949 Vector<double> zeta(1);
19950 zeta[0] = zeta_coordinates[i];
19951 new_nod_pt->set_coordinates_on_boundary(
19952 original_boundaries_node_is_on[i], zeta);
19953 }
19954
19955 } // if (node is on an original boundary)
19956 else
19957 {
19958 // Check if necessary to create the node, or if it has been
19959 // already found in shared boundaries with other processors
19960 if (!found_node_in_other_shared_boundaries)
19961 {
19962 // Construct an ordinary (non-boundary) node
19963 if (time_stepper_pt != 0)
19964 {
19965 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
19966 }
19967 else
19968 {
19969 new_nod_pt = new_el_pt->construct_node(node_index);
19970 }
19971 } // if (!found_node_in_other_shared_boundaries)
19972 else
19973 {
19974 // If the node was found then assign the node to the element
19975 new_el_pt->node_pt(node_index) = new_nod_pt;
19976 } // else if (!found_node_in_other_shared_boundaries)
19977
19978 } // else (the node is not a boundary node)
19979
19980 // ... and gather all its information
19981
19982 // If the node was found or not in other shared boundaries, this
19983 // is the first time the node is received from this processor
19984 // (iproc), therefore it is added to the vector of nodes received
19985 // from this processor (iproc)
19986 new_nodes_on_domain.push_back(new_nod_pt);
19987
19988 // Check if necessary to state all the info. to the node if it has
19989 // been already found in shared boundaries with other processors
19990 if (!found_node_in_other_shared_boundaries)
19991 {
19992 // Add the node to the general node storage
19993 this->add_node_pt(new_nod_pt);
19994 } // if (!found_node_in_other_shared_boundaries)
19995
19996 // Is the new constructed node Algebraic?
19997 AlgebraicNode* new_alg_nod_pt = dynamic_cast<AlgebraicNode*>(new_nod_pt);
19998
19999 // If it is algebraic, its node update functions will
20000 // not yet have been set up properly
20001 if (new_alg_nod_pt != 0)
20002 {
20003 // The AlgebraicMesh is the external mesh
20004 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
20005
20006 /// The first entry of All_alg_nodal_info contains
20007 /// the default node update id
20008 /// e.g. for the quarter circle there are
20009 /// "Upper_left_box", "Lower right box" etc...
20010#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20012 << " Alg node update id "
20014 << std::endl;
20015#endif
20016
20017 unsigned update_id =
20019
20020 Vector<double> ref_value;
20021
20022 // The size of this vector is in the next entry
20023 // of All_alg_nodal_info
20024#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20026 << " Alg node # of ref values "
20028 << std::endl;
20029#endif
20030 unsigned n_ref_val =
20032
20033 // The reference values themselves are in
20034 // All_alg_ref_value
20035 ref_value.resize(n_ref_val);
20036 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
20037 {
20038 ref_value[i_ref] =
20040 }
20041
20042 Vector<GeomObject*> geom_object_pt;
20043 /// again we need the size of this vector as it varies
20044 /// between meshes; we also need some indication
20045 /// as to which geometric object should be used...
20046
20047 // The size of this vector is in the next entry
20048 // of All_alg_nodal_info
20049#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20051 << " Alg node # of geom objects "
20053 << std::endl;
20054#endif
20055 unsigned n_geom_obj =
20057
20058 // The remaining indices are in the rest of
20059 // All_alg_nodal_info
20060 geom_object_pt.resize(n_geom_obj);
20061 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
20062 {
20063#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20065 << " Alg node: geom object index "
20067 << std::endl;
20068#endif
20069 unsigned geom_index =
20071 // This index indicates which of the AlgebraicMesh's
20072 // stored geometric objects should be used
20073 // (0 is a null pointer; everything else should have
20074 // been filled in by the specific Mesh). If it
20075 // hasn't been filled in then the update_node_update
20076 // call should fix it
20077 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
20078 }
20079
20080 // Check if necessary to state all the info. to the node if it has
20081 // been already found in shared boundaries with other processors
20082 if (!found_node_in_other_shared_boundaries)
20083 {
20084 /// For the received update_id, ref_value, geom_object
20085 /// call add_node_update_info
20086 new_alg_nod_pt->add_node_update_info(
20087 update_id, alg_mesh_pt, geom_object_pt, ref_value);
20088
20089 /// Now call update_node_update
20090 alg_mesh_pt->update_node_update(new_alg_nod_pt);
20091
20092 } // if (!found_node_in_other_shared_boundaries)
20093
20094 } // if (new_alg_nod_pt!=0)
20095
20096 // Check if necessary to state all the info. to the node if it has
20097 // been already found in shared boundaries with other processors
20098 if (!found_node_in_other_shared_boundaries)
20099 {
20100 // Is the node a MacroElementNodeUpdateNode?
20101 MacroElementNodeUpdateNode* macro_nod_pt =
20102 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
20103
20104 if (macro_nod_pt != 0)
20105 {
20106 // Need to call set_node_update_info; this requires
20107 // a Vector<GeomObject*> (taken from the mesh)
20108 Vector<GeomObject*> geom_object_vector_pt;
20109
20110 // Access the required geom objects from the
20111 // MacroElementNodeUpdateMesh
20112 MacroElementNodeUpdateMesh* macro_mesh_pt =
20113 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
20114 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
20115
20116 // Get local coordinate of node in new element
20117 Vector<double> s_in_macro_node_update_element;
20118 new_el_pt->local_coordinate_of_node(node_index,
20119 s_in_macro_node_update_element);
20120
20121 // Set node update info for this node
20122 macro_nod_pt->set_node_update_info(
20123 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
20124 }
20125
20126 } // if (!found_node_in_other_shared_boundaries)
20127
20128 // If there are additional values, resize the node
20129 unsigned n_new_val = new_nod_pt->nvalue();
20130
20131 // Check if necessary to state all the info. to the node if it has
20132 // been already found in shared boundaries with other processors
20133 if (!found_node_in_other_shared_boundaries)
20134 {
20135 if (n_val > n_new_val)
20136 {
20137 // If it has been necessary to resize then it may be becuse
20138 // the node is on a FSI boundary, if that is the case we need
20139 // to set a map for these external values
20140
20141 // Cast to a boundary node
20142 BoundaryNodeBase* bnod_pt =
20143 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
20144
20145 // Create storage, if it doesn't already exist, for the map
20146 // that will contain the position of the first entry of
20147 // this face element's additional values,
20149 {
20151 new std::map<unsigned, unsigned>;
20152 }
20153
20154 // Get pointer to the map
20155 std::map<unsigned, unsigned>* map_pt =
20157
20158 // The id of the face to which this node belong in the bulk
20159 // element
20160 const unsigned id_face = 0;
20161 // We only resize the node values Vector if we haven't done it yet
20162 std::map<unsigned, unsigned>::const_iterator p =
20163 map_pt->find(id_face);
20164
20165 // If this node hasn't been resized for current id
20166 if (p == map_pt->end())
20167 {
20168 // assign the face element id and the position of the
20169 // first entry to the boundary node
20170 (*map_pt)[id_face] = n_new_val;
20171
20172 // resize the node vector of values
20173 new_nod_pt->resize(n_val);
20174 }
20175
20176 } // if (n_val>n_new_val)
20177
20178 } // if (!found_node_in_other_shared_boundaries)
20179
20180 // Is the new node a SolidNode?
20181 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
20182 if (solid_nod_pt != 0)
20183 {
20184 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
20185 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
20186 {
20187 for (unsigned t = 0; t < n_prev; t++)
20188 {
20189 double read_data =
20191
20192 // Check if necessary to state all the info. to the node if it has
20193 // been already found in shared boundaries with other processors
20194 if (!found_node_in_other_shared_boundaries)
20195 {
20196 solid_nod_pt->variable_position_pt()->set_value(
20197 t, i_val, read_data);
20198 } // if (!found_node_in_other_shared_boundaries)
20199 }
20200 }
20201
20202#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION
20204 << " Number of values solid node: "
20206 << std::endl;
20207#endif
20208 const unsigned nvalues_solid_node =
20210 Vector<double> values_solid_node(nvalues_solid_node);
20211 for (unsigned i = 0; i < nvalues_solid_node; i++)
20212 {
20213 values_solid_node[i] =
20215 }
20216
20217 // Check if necessary to state all the info. to the node if it has
20218 // been already found in shared boundaries with other processors
20219 if (!found_node_in_other_shared_boundaries)
20220 {
20221 unsigned index = 0;
20222 solid_nod_pt->read_values_from_vector(values_solid_node, index);
20223 }
20224 }
20225
20226 // Get copied history values
20227 // unsigned n_val=new_nod_pt->nvalue();
20228 for (unsigned i_val = 0; i_val < n_val; i_val++)
20229 {
20230 for (unsigned t = 0; t < n_prev; t++)
20231 {
20232 double read_data =
20234
20235 // Check if necessary to state all the info. to the node if it
20236 // has been already found in shared boundaries with other
20237 // processors
20238 if (!found_node_in_other_shared_boundaries)
20239 {
20240 new_nod_pt->set_value(t, i_val, read_data);
20241 } // if (!found_node_in_other_shared_boundaries)
20242 }
20243 }
20244
20245 // Get copied history values for positions
20246 unsigned n_dim = new_nod_pt->ndim();
20247 for (unsigned idim = 0; idim < n_dim; idim++)
20248 {
20249 for (unsigned t = 0; t < n_prev; t++)
20250 {
20251 double read_data =
20253
20254 // Check if necessary to state all the info. to the node if it
20255 // has been already found in shared boundaries with other
20256 // processors
20257 if (!found_node_in_other_shared_boundaries)
20258 {
20259 // Copy to coordinate
20260 new_nod_pt->x(t, idim) = read_data;
20261
20262 } // if (!found_node_in_other_shared_boundaries)
20263 }
20264 }
20265
20266 } // if (is_node_on_shared_boundary != 1)
20267
20268 // If the node was not found in other shared boundaries (possibly
20269 // because it is the first time the node has been sent) then copy
20270 // the node to the shared boundaries where it should be, use the
20271 // special container for this cases
20272 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
20273 // shared
20274 // boundaries with
20275 // other processors
20276 !found_node_in_other_shared_boundaries) // The node has not
20277 // been previously
20278 // set as
20279 // shared with
20280 // other processors
20281 // (first time)
20282 {
20283 // Update the node pointer in all the (references) of the node
20284 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
20285 other_proc_shd_bnd_node_pt,
20286 other_processor_1,
20287 other_processor_2,
20288 other_shared_boundaries,
20289 other_indexes,
20290 global_node_names,
20291 node_name_to_global_index,
20292 global_shared_node_pt);
20293
20294 } // if (!found_node_in_other_shared_boundaries)
20295 }
20296
20297 //========start of update_other_proc_shd_bnd_node_helper=================
20298 // Helper function that assigns/updates the references to the node so
20299 // that it can be found with any other reference
20300 //========================================================================
20301 template<class ELEMENT>
20303 Node*& new_node_pt,
20304 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
20305 other_proc_shd_bnd_node_pt,
20306 Vector<unsigned>& other_processor_1,
20307 Vector<unsigned>& other_processor_2,
20308 Vector<unsigned>& other_shared_boundaries,
20309 Vector<unsigned>& other_indexes,
20310 Vector<Vector<Vector<unsigned>>>& global_node_names,
20311 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
20312 Vector<Node*>& global_shared_node_pt)
20313 {
20314 // Get the number of initial shared boundaries to correct the index
20315 // of the shared boundary
20316 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
20317
20318#ifdef PARANOID
20319 // Get the number of instances of the node on other shared
20320 // boundaries with other processors
20321 const unsigned n_data = other_processor_1.size();
20322#endif // #ifdef PARANOID
20323
20324 // Create the first node name
20325 Vector<unsigned> node_name(4);
20326 node_name[0] = other_processor_1[0];
20327 node_name[1] = other_processor_2[0];
20328 node_name[2] = other_shared_boundaries[0];
20329 node_name[3] = other_indexes[0];
20330
20331#ifdef PARANOID
20332 // Get the global node index, and all the names of the node
20333 std::map<Vector<unsigned>, unsigned>::iterator it =
20334 node_name_to_global_index.find(node_name);
20335 if (it == node_name_to_global_index.end())
20336 {
20337 std::ostringstream error_stream;
20338 error_stream << "The node name does not exist in the global node names\n"
20339 << "This is the name of the node\n"
20340 << "Name: iproc, jproc, ishd_bnd, idx\n"
20341 << "Name: " << node_name[0] << ", " << node_name[1] << ", "
20342 << node_name[2] << ", " << node_name[3] << "\n";
20343 throw OomphLibError(
20344 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20345 } // if (it!=node_name_to_global_index.end())
20346#endif // #ifdef PARANOID
20347
20348 // Get the global node index
20349 const unsigned iglobal_node = node_name_to_global_index[node_name];
20350 // Add the node to the global shared node container
20351 global_shared_node_pt[iglobal_node] = new_node_pt;
20352 // Get the names
20353 Vector<Vector<unsigned>> inode_names = global_node_names[iglobal_node];
20354 // Get the number of names of the node
20355 const unsigned n_names = inode_names.size();
20356
20357#ifdef PARANOID
20358 // Check that the received names of the node are part of the global
20359 // node names
20360 unsigned n_found_node_names_on_global_node_name = 0;
20361 // loop over the input node names
20362 for (unsigned j = 0; j < n_data; j++)
20363 {
20364 // loop over the inode_names
20365 for (unsigned k = 0; k < n_names; k++)
20366 {
20367 // Is this input name part of the global node names?
20368 if (inode_names[k][0] == other_processor_1[j] &&
20369 inode_names[k][1] == other_processor_2[j] &&
20370 inode_names[k][2] == other_shared_boundaries[j] &&
20371 inode_names[k][3] == other_indexes[j])
20372 {
20373 // Increase the number of found input node names in the
20374 // global node names
20375 n_found_node_names_on_global_node_name++;
20376 }
20377
20378 } // for (k < n_names)
20379
20380 } // for (j < n_data)
20381
20382 // Were all the input node names found on the global node names?
20383 if (n_found_node_names_on_global_node_name != n_data)
20384 {
20385 std::ostringstream error_stream;
20386 error_stream
20387 << "Not all the node names of the current node were found on the\n"
20388 << "global node names. This happened when adding the node pointer\n"
20389 << "to the data structure that keeps tracks of nodes on shared\n"
20390 << "boundaries with other processors\n\n"
20391 << "These are the names of the current node\n"
20392 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20393 for (unsigned j = 0; j < n_data; j++)
20394 {
20395 error_stream << "Name(" << j << "): " << other_processor_1[j] << ", "
20396 << other_processor_2[j] << ", "
20397 << other_shared_boundaries[j] << ", " << other_indexes[j]
20398 << "\n";
20399 }
20400
20401 error_stream << "\n\nThese are the names of the global node\n"
20402 << "Name k: iproc, jproc, ishd_bnd, idx\n";
20403 for (unsigned k = 0; k < n_names; k++)
20404 {
20405 error_stream << "Name(" << k << "): " << inode_names[k][0] << ", "
20406 << inode_names[k][1] << ", " << inode_names[k][2] << ", "
20407 << inode_names[k][3] << "\n";
20408 }
20409
20410 throw OomphLibError(
20411 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20412 }
20413#endif // #ifdef PARANOID
20414
20415 // Set the node pointer in all of its names
20416 for (unsigned j = 0; j < n_names; j++)
20417 {
20418 // Get the j-th node name
20419 const unsigned iproc = inode_names[j][0];
20420 const unsigned jproc = inode_names[j][1];
20421 const unsigned ishd_bnd = inode_names[j][2] - initial_shd_bnd_id;
20422 const unsigned index = inode_names[j][3];
20423
20424 // The info. is stored only in one direction
20425 // Get the smallest processor number
20426 if (iproc < jproc)
20427 {
20428 other_proc_shd_bnd_node_pt[iproc][jproc][ishd_bnd][index] = new_node_pt;
20429 }
20430 else
20431 {
20432 other_proc_shd_bnd_node_pt[jproc][iproc][ishd_bnd][index] = new_node_pt;
20433 }
20434
20435 } // for (j < n_names)
20436 }
20437
20438 // *********************************************************************
20439 // End communication functions
20440 // *********************************************************************
20441
20442 // *********************************************************************
20443 // BEGIN: Methods to perform load balance
20444 // *********************************************************************
20445
20446 //======================================================================
20447 /// Performs the load balancing for unstructured meshes, the
20448 /// load balancing strategy is based on mesh migration
20449 //======================================================================
20450 template<class ELEMENT>
20452 const Vector<unsigned>& target_domain_for_local_non_halo_element)
20453 {
20454 oomph_info << "Load balance (unstructured mesh) [BEGIN]" << std::endl;
20455
20456 // This method can only be called when the mesh has been already
20457 // distributed
20458 if (!this->is_mesh_distributed())
20459 {
20460 std::ostringstream warning_message;
20461 warning_message
20462 << "\n===============================================================\n"
20463 << "The load balancing can only be performed in distributed meshes,\n"
20464 << "your mesh has not been distributed.\n"
20465 << "==============================================================="
20466 "\n\n";
20467 OomphLibWarning(warning_message.str(),
20468 OOMPH_CURRENT_FUNCTION,
20469 OOMPH_EXCEPTION_LOCATION);
20470 // Return
20471 return;
20472 }
20473
20474 // Get the number of processors
20475 const unsigned nproc = this->communicator_pt()->nproc();
20476 // Get the rank of the current processors
20477 const unsigned my_rank = this->communicator_pt()->my_rank();
20478
20479 // Check that there are at least two processors
20480 if (nproc == 1)
20481 {
20482 std::ostringstream warning_message;
20483 warning_message
20484 << "\n===============================================================\n"
20485 << "The load balancing can only be performed when there are at least\n"
20486 << "two procesors, the current number of processors is one.\n"
20487 << "==============================================================="
20488 "\n\n";
20489 OomphLibWarning(warning_message.str(),
20490 OOMPH_CURRENT_FUNCTION,
20491 OOMPH_EXCEPTION_LOCATION);
20492 // Return
20493 return;
20494 }
20495
20496 // Get the time before load balance
20497 double t_start_overall_load_balance = 0.0;
20498 if (Print_timings_level_load_balance > 1)
20499 {
20500 t_start_overall_load_balance = TimingHelpers::timer();
20501 }
20502
20503 // Get the number of elements in the mesh before load balance
20504 const unsigned nelement_before_load_balance = this->nelement();
20505
20506#ifdef PARANOID
20507 // The number of elements in the mesh and the number of target
20508 // domains for the local non halo elements in the mesh should match
20509 if (nnon_halo_element() != target_domain_for_local_non_halo_element.size())
20510 {
20511 std::ostringstream error_message;
20512 error_message << "The number of non halo elements in the current mesh ("
20513 << nnon_halo_element() << ") and the number\n"
20514 << "of target areas for the local non halo elements ("
20515 << target_domain_for_local_non_halo_element.size()
20516 << ") is different\n\n";
20517 throw OomphLibError(
20518 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
20519 }
20520#endif
20521
20522 // Backup pointers to elements in this mesh
20523 Vector<FiniteElement*> backed_up_ele_pt(nelement_before_load_balance);
20524 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20525 {
20526 backed_up_ele_pt[e] = this->finite_element_pt(e);
20527 }
20528
20529 // =====================================================================
20530 // BEGIN: GET THE DOMAINS FOR THE HALO ELEMENTS
20531 // =====================================================================
20532
20533 // Get the time to get the domains of halo elements
20534 double tt_start_get_domains_halo_elements = 0.0;
20535 if (Print_timings_level_load_balance > 1)
20536 {
20537 tt_start_get_domains_halo_elements = TimingHelpers::timer();
20538 }
20539
20540 // Get the new domains for the halo elements
20541
20542 // Send the new domains for the current haloed elements, and receive
20543 // the new domains for the current halo elements
20544 // -- 1) On the current processor get the new domains for the
20545 // haloed elements
20546 // -- 2) Then send this info. to all the processor that have a
20547 // halo copy of the element
20548
20549 // The storing for the new domains of the haloed elements (sent to
20550 // other processors)
20551 Vector<Vector<unsigned>> new_domains_haloed_elements(nproc);
20552 // The storing for the new domains of the halo elements (received
20553 // from other processors)
20554 Vector<Vector<unsigned>> new_domains_halo_elements(nproc);
20555
20556 // First resize the containers by getting the current number of
20557 // halo/haloed elements within each processor
20558 for (unsigned iproc = 0; iproc < nproc; iproc++)
20559 {
20560 // There are no halo/haloed elements with myself (my_rank
20561 // processor)
20562 if (iproc != my_rank)
20563 {
20564 // Get the number of halo elements with iproc processor
20565 const unsigned n_halo_iproc = this->nroot_halo_element(iproc);
20566 // Resize the container
20567 new_domains_halo_elements[iproc].resize(n_halo_iproc);
20568
20569 // Get the number of haloed elements with iproc processor
20570 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20571 // Resize the container
20572 new_domains_haloed_elements[iproc].resize(n_haloed_iproc);
20573 } // if (iproc != my_rank)
20574 } // for (iproc < nproc)
20575
20576#ifdef PARANOID
20577 // Count the number of found haloed elements
20578 Vector<unsigned> counter_for_found_haloed_elements(nproc, 0);
20579#endif
20580
20581 // Go through all the haloed elements and find their new domain
20582
20583 // Get the haloed elements with in each processor and check if the
20584 // element is haloed with the processor
20585 for (unsigned iproc = 0; iproc < nproc; iproc++)
20586 {
20587 // There are no halo/haloed elements with myself (my_rank
20588 // processor)
20589 if (iproc != my_rank)
20590 {
20591 // Get the number of haloed elements with iproc processor
20592 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20593
20594 // Loop over the haloed elements
20595 for (unsigned ihd = 0; ihd < n_haloed_iproc; ihd++)
20596 {
20597 // Get the ihd-th haloed element with "iproc" processor
20598 GeneralisedElement* haloed_ele_pt =
20599 this->root_haloed_element_pt(iproc, ihd);
20600
20601 // The counter for the nonhalo elements
20602 unsigned nh_count4 = 0;
20603 // Find the element in the general elements container
20604 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20605 {
20606 // Get the e-th element
20607 GeneralisedElement* ele_pt = this->element_pt(e);
20608 // Check if the element is a nonhalo element
20609 if (!ele_pt->is_halo())
20610 {
20611 // Increase the counter for nonhalo elements, in case the
20612 // haloed element is found get the (nh_count4-1) position
20613 // in the target domains vector
20614 nh_count4++;
20615
20616 if (ele_pt == haloed_ele_pt)
20617 {
20618 // Get the new domain for this element
20619 const unsigned element_domain =
20620 target_domain_for_local_non_halo_element[nh_count4 - 1];
20621 // Here decrease the counter ---------------------^
20622
20623 // Set the new domain for the haloed element in the
20624 // special container
20625 new_domains_haloed_elements[iproc][ihd] = element_domain;
20626#ifdef PARANOID
20627 // Increase the counter
20628 counter_for_found_haloed_elements[iproc]++;
20629#endif
20630 // ... and break the "for" with the general
20631 // elements. Continue with the next haloed element
20632 break;
20633
20634 } // if (ele_pt == haloed_ele_pt))
20635
20636 } // if (!ele_pt->is_halo())
20637
20638 } // for (e < nelement_before_load_balance)
20639
20640 } // for (ihd < n_haloed_iproc)
20641
20642 } // if (iproc != my_rank)
20643
20644 } // for (iproc < nproc)
20645
20646#ifdef PARANOID
20647 // Check that all the haloed elements with all processors have been
20648 // found
20649 for (unsigned iproc = 0; iproc < nproc; iproc++)
20650 {
20651 // There are no halo/haloed elements with myself (my_rank
20652 // processor)
20653 if (iproc != my_rank)
20654 {
20655 // Get the number of haloed elements with "iproc" processor
20656 const unsigned n_haloed_iproc = this->nroot_haloed_element(iproc);
20657
20658 // Compare the number of found haloed elements with the current
20659 // number of haloed elements
20660 if (n_haloed_iproc != counter_for_found_haloed_elements[iproc])
20661 {
20662 std::ostringstream error_message;
20663 error_message << "The independent counting of found haloed elements ("
20664 << counter_for_found_haloed_elements[iproc]
20665 << ") with processor (" << iproc
20666 << ") is not equal to the number of haloed elements ("
20667 << n_haloed_iproc << ") with processor (" << iproc
20668 << ")\n";
20669 throw OomphLibError(error_message.str(),
20670 OOMPH_CURRENT_FUNCTION,
20671 OOMPH_EXCEPTION_LOCATION);
20672 } // if (nhaloed_iproc == counter_for_found_haloed_elements[iproc])
20673
20674 } // if (iproc != my_rank)
20675
20676 } // for (iproc < nproc)
20677#endif
20678
20679 // Now we have the new domains for the haloed elements
20680
20681 // Send this info. to the processor with a halo copy of the haloed
20682 // elements and set the new domains in the halo copies
20683
20684 // First put all the info. in a flat package array
20685 Vector<unsigned> new_domains_haloed_flat_unsigned;
20686 // Put in a vector the number of haloed elements within each
20687 // processor
20688 Vector<int> nhaloed_elements_with_iproc(nproc);
20689 for (unsigned iproc = 0; iproc < nproc; iproc++)
20690 {
20691 // There are no halo/haloed elements with myself (my_rank
20692 // processor)
20693 if (iproc != my_rank)
20694 {
20695 // Get the number of haloed elements with "iproc" processor
20696 const unsigned n_haloed_ele_iproc = this->nroot_haloed_element(iproc);
20697 // Copy the number of haloed elements with "iproc" processor
20698 nhaloed_elements_with_iproc[iproc] = n_haloed_ele_iproc;
20699 // Copy the new domains of the haloed elements in the flat
20700 // package
20701 for (unsigned i = 0; i < n_haloed_ele_iproc; i++)
20702 {
20703 new_domains_haloed_flat_unsigned.push_back(
20704 new_domains_haloed_elements[iproc][i]);
20705 } // for (i < n_haloed_ele_iproc)
20706
20707 } // if (iproc != my_rank)
20708
20709 } // for (iproc < nproc)
20710
20711 // The offsets of the flat package within each processor
20712 Vector<int> offset_haloed_elements_with_iproc(nproc);
20713 offset_haloed_elements_with_iproc[0] = 0;
20714 for (unsigned ip = 1; ip < nproc; ip++)
20715 {
20716 // Compute the offset to send the values to each processor
20717 offset_haloed_elements_with_iproc[ip] =
20718 offset_haloed_elements_with_iproc[ip - 1] +
20719 nhaloed_elements_with_iproc[ip - 1];
20720 } // for (ip < nproc)
20721
20722 // Prepare to receive the data
20723
20724 // Compute the number of data (halo elements) to receive from each
20725 // processor and the displacements within each processor
20726
20727 // Counter for the total number of halo elements within all processors
20728 unsigned counter_halo_ele_with_all_procs = 0;
20729
20730 // Put in a vector the number of halo elements expected to receive
20731 // from each processor
20732 Vector<int> nhalo_elements_with_iproc(nproc);
20733 // Compute the number of total halo elements of (my_rank) this
20734 // processor with all other processors
20735 for (unsigned iproc = 0; iproc < nproc; iproc++)
20736 {
20737 // There are no halo/haloed elements with myself (my_rank
20738 // processor)
20739 if (iproc != my_rank)
20740 {
20741 // Get the number of halo elements with "iproc" processor
20742 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20743 // Copy the number of halo elements with "iproc" processor
20744 nhalo_elements_with_iproc[iproc] = n_halo_ele_iproc;
20745 // Add the number of elements with this processor
20746 counter_halo_ele_with_all_procs += n_halo_ele_iproc;
20747 } // if (iproc != my_rank)
20748
20749 } // for (iproc < nproc)
20750
20751 // The offsets of the flat package within each processor
20752 Vector<int> offset_halo_elements_with_iproc(nproc);
20753 offset_halo_elements_with_iproc[0] = 0;
20754 for (unsigned ip = 1; ip < nproc; ip++)
20755 {
20756 // Compute the offset to receive the values from each processor
20757 offset_halo_elements_with_iproc[ip] =
20758 offset_halo_elements_with_iproc[ip - 1] +
20759 nhalo_elements_with_iproc[ip - 1];
20760 } // for (ip < nproc)
20761
20762 // The flat container to receive the new domains of the halo
20763 // elements in the current processor
20764
20765 // The flat package where all the info. will be gather from the
20766 // other processors (the halo flat package)
20767 Vector<unsigned> new_domains_halo_flat_unsigned(
20768 counter_halo_ele_with_all_procs);
20769
20770 // Perform the sending and receiving of information to and from all
20771 // processors
20772 MPI_Alltoallv(&new_domains_haloed_flat_unsigned[0], // void *sendbuf
20773 &nhaloed_elements_with_iproc[0], // int *sendcnts
20774 &offset_haloed_elements_with_iproc[0], // int *sdispls
20775 MPI_UNSIGNED, // MPI_Datatype sendtype
20776 &new_domains_halo_flat_unsigned[0], // void *recvbuf
20777 &nhalo_elements_with_iproc[0], // int *recvcnts
20778 &offset_halo_elements_with_iproc[0], // int *rdispls
20779 MPI_UNSIGNED, // MPI_Datatype recvtype
20780 this->communicator_pt()->mpi_comm()); // MPI_Comm comm
20781
20782 // Once received the new domains for the halo elements, copy the
20783 // domains back to an easier to handle container (from the flat
20784 // package to the one with the different halo elements domains
20785 // within each processor)
20786 unsigned counter_new_domains_halo_ele = 0;
20787 for (unsigned iproc = 0; iproc < nproc; iproc++)
20788 {
20789 // There are no halo/haloed elements with myself (my_rank
20790 // processor)
20791 if (iproc != my_rank)
20792 {
20793 // Get the number of halo elements with "iproc"
20794 const unsigned ntmp_halo_elements_with_iproc =
20795 nhalo_elements_with_iproc[iproc];
20796 // Loop over the number of halo elements within "iproc" and copy
20797 // the elements from the flat package
20798 for (unsigned i = 0; i < ntmp_halo_elements_with_iproc; i++)
20799 {
20800 // Copy the new domain of the halo elements from the flat
20801 // package to an easier to use container
20802 new_domains_halo_elements[iproc][i] =
20803 new_domains_halo_flat_unsigned[counter_new_domains_halo_ele++];
20804 }
20805 } // if (iproc != my_rank)
20806 } // for (iproc < nproc)
20807
20808 // The time to get domains of halo elements
20809 if (Print_timings_level_load_balance > 1)
20810 {
20811 oomph_info << "CPU for getting domains halo elements (load balance) [1]: "
20812 << TimingHelpers::timer() - tt_start_get_domains_halo_elements
20813 << std::endl;
20814 }
20815
20816 // =====================================================================
20817 // END: GET THE DOMAINS FOR THE HALO ELEMENTS
20818 // =====================================================================
20819
20820 // =====================================================================
20821 // BEGIN: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20822 // ELEMENTS
20823 // =====================================================================
20824
20825 // Get the time to get FiniteElement versions from Generalised
20826 // halo(ed) elements
20827 double tt_start_get_fe_version_from_ge_halo_ed = 0.0;
20828 if (Print_timings_level_load_balance > 1)
20829 {
20830 tt_start_get_fe_version_from_ge_halo_ed = TimingHelpers::timer();
20831 }
20832
20833 // The finite element storage for the halo elements
20834 Vector<Vector<FiniteElement*>> f_halo_element_pt(nproc);
20835 // The finite element storage for the haloed elements
20836 Vector<Vector<FiniteElement*>> f_haloed_element_pt(nproc);
20837 // Loop over the processors
20838 for (unsigned iproc = 0; iproc < nproc; iproc++)
20839 {
20840 // There are no halo(ed) elements with myself
20841 if (iproc != my_rank)
20842 {
20843 // Get the number of halo elements with the "iproc" processor
20844 const unsigned nhalo_ele_iproc = this->nroot_halo_element(iproc);
20845 // Get the halo elements with the "iproc" processor
20846 Vector<GeneralisedElement*> halo_element_pt_iproc =
20847 this->root_halo_element_pt(iproc);
20848 // Resize the finite element container
20849 f_halo_element_pt[iproc].resize(nhalo_ele_iproc);
20850 // Loop over the halo elements
20851 for (unsigned ih = 0; ih < nhalo_ele_iproc; ih++)
20852 {
20853 // Get the finite element
20854 FiniteElement* ele_pt =
20855 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20856 // Store the finite element version of the element
20857 f_halo_element_pt[iproc][ih] = ele_pt;
20858 } // for (ih < nhalo_ele_iproc)
20859
20860 // Get the number of haloed elements with the "iproc" processor
20861 const unsigned nhaloed_ele_iproc = this->nroot_haloed_element(iproc);
20862 // Get the haloed elements with the "iproc" processor
20863 Vector<GeneralisedElement*> haloed_element_pt_iproc =
20864 this->root_haloed_element_pt(iproc);
20865 // Resize the finite element container
20866 f_haloed_element_pt[iproc].resize(nhaloed_ele_iproc);
20867 // Loop over the haloed elements
20868 for (unsigned ihd = 0; ihd < nhaloed_ele_iproc; ihd++)
20869 {
20870 // Get the finite element
20871 FiniteElement* ele_pt =
20872 dynamic_cast<FiniteElement*>(haloed_element_pt_iproc[ihd]);
20873 // Store the finite element version of the element
20874 f_haloed_element_pt[iproc][ihd] = ele_pt;
20875 } // for (ih < nhaloed_ele_iproc)
20876
20877 } // if (iproc != my_rank)
20878
20879 } // for (iproc < nproc)
20880
20881 // The time to get FiniteElement versions from Generalised halo(ed)
20882 // elements
20883 if (Print_timings_level_load_balance > 1)
20884 {
20885 oomph_info << "CPU for getting finite element versions from generalised "
20886 "halo(ed) elements (load balance) [2]: "
20888 tt_start_get_fe_version_from_ge_halo_ed
20889 << std::endl;
20890 }
20891
20892 // =====================================================================
20893 // END: CREATE FINITE ELEMENT LOCAL VERSIONS OF THE HALO(ED)
20894 // ELEMENTS
20895 // =====================================================================
20896
20897 // =====================================================================
20898 // BEGIN: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
20899 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
20900 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
20901 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
20902 // =====================================================================
20903
20904 // Get the time to prepare elements to send to other processors
20905 double tt_start_prepare_element_to_send = 0.0;
20906 if (Print_timings_level_load_balance > 1)
20907 {
20908 tt_start_prepare_element_to_send = TimingHelpers::timer();
20909 }
20910
20911 // Store the elements that will be sent to other processors
20912 Vector<Vector<FiniteElement*>> elements_to_send_pt(nproc);
20913
20914 // Associate the nodes of each element with the processor the
20915 // element will live on
20916 std::map<Data*, std::set<unsigned>>
20917 processors_associated_with_data_before_load_balance;
20918
20919 // Compute the elements that will be sent to other processor and
20920 // associate the nodes with the processor the element will live on
20921 unsigned nh_count3 = 0;
20922 for (unsigned e = 0; e < nelement_before_load_balance; e++)
20923 {
20924 // Get the element
20925 FiniteElement* ele_pt = this->finite_element_pt(e);
20926 // Only work with nonhalo elements
20927 if (!(ele_pt->is_halo()))
20928 {
20929 // Get the new domain for the elment
20930 const unsigned element_domain =
20931 target_domain_for_local_non_halo_element[nh_count3++];
20932
20933 // Include the element in the corresponding vector
20934 elements_to_send_pt[element_domain].push_back(ele_pt);
20935
20936 // Get the number of nodes on the element
20937 const unsigned n_nodes = ele_pt->nnode();
20938 // Loop over the nodes
20939 for (unsigned j = 0; j < n_nodes; j++)
20940 {
20941 // Get each node of the element
20942 Node* node_pt = ele_pt->node_pt(j);
20943 // ... and associate it with element domains
20944 processors_associated_with_data_before_load_balance[node_pt].insert(
20945 element_domain);
20946
20947 } // for (j < n_nodes)
20948
20949 } // if (!(ele_pt->is_halo()))
20950
20951 } // for (e < nelement_before_load_balance)
20952
20953 // ... do the same for the halo elements (but do not add them to the
20954 // sending container since only the processor with the haloed
20955 // counterparts is in charge of that). Associate the nodes of the
20956 // halo elements with the processor they will live on
20957 for (unsigned iproc = 0; iproc < nproc; iproc++)
20958 {
20959 // There is no halo elements with myself
20960 if (iproc != my_rank)
20961 {
20962 // Get the number of halo elements with the "iproc" processor
20963 const unsigned n_halo_ele_iproc = this->nroot_halo_element(iproc);
20964 // Get the halo elements with the "iproc" processor
20965 Vector<GeneralisedElement*> halo_element_pt_iproc =
20966 this->root_halo_element_pt(iproc);
20967 // Loop over the halo elements with iproc
20968 for (unsigned ih = 0; ih < n_halo_ele_iproc; ih++)
20969 {
20970 // Get the new domain for the halo element
20971 const unsigned element_domain = new_domains_halo_elements[iproc][ih];
20972
20973 // Get the finite element
20974 FiniteElement* ele_pt =
20975 dynamic_cast<FiniteElement*>(halo_element_pt_iproc[ih]);
20976
20977 // Get the number of nodes on the halo element
20978 const unsigned n_nodes = ele_pt->nnode();
20979 // Loop over the nodes
20980 for (unsigned j = 0; j < n_nodes; j++)
20981 {
20982 // Get each node of the halo element
20983 Node* node_pt = ele_pt->node_pt(j);
20984
20985 // ... and associate it with element domains
20986 processors_associated_with_data_before_load_balance[node_pt].insert(
20987 element_domain);
20988
20989 } // for (j < n_nodes)
20990
20991 } // for (ih < nhalo_ele_iproc)
20992
20993 } // if (iproc != my_rank)
20994
20995 } // for (iproc < nproc)
20996
20997 // The time to prepare elements to send to other processors
20998 if (Print_timings_level_load_balance > 1)
20999 {
21000 oomph_info << "CPU for preparing elements to send to other processors "
21001 "(load balance) [3]: "
21002 << TimingHelpers::timer() - tt_start_prepare_element_to_send
21003 << std::endl;
21004 }
21005
21006 // Now all the nodes are associated with the processor where the
21007 // element will live on. This is performed for the nonhalo and halo
21008 // elements
21009
21010 // =====================================================================
21011 // END: 1) PREPARE THE ELEMENTS THAT WILL BE SENT TO OTHER PROCESSORS
21012 // ---- HALO ELEMENTS ARE NOT CONSIDERED FOR SENDING
21013 // 2) ASSOCIATE THE NODES WITH THE NEW DOMAIN OF THE ELEMENTS
21014 // ---- THE SAME IS PERFORMED FOR NODES IN HALO ELEMENTS
21015 // =====================================================================
21016
21017 // =====================================================================
21018 // BEGIN: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21019 // CURRENT PROCESSOR
21020 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21021 // =====================================================================
21022
21023 // Get the time to compute new local halo elements within all
21024 // processors
21025 double tt_start_compute_new_local_halo_elements = 0.0;
21026 if (Print_timings_level_load_balance > 1)
21027 {
21028 tt_start_compute_new_local_halo_elements = TimingHelpers::timer();
21029 }
21030
21031 // Before sending the elements across compute the new local
21032 // halo/haloed elements of each processor. Each processor could have
21033 // elements that will be part of the new halo/haloed elements of
21034 // another processors, then these processors need to compute the
21035 // relations that may happen among these other processors
21036
21037 // Example:
21038 // Processor 1 may have elements that will be sent to processor 3
21039 // and 4. These processors need to know about the new halo elements
21040 // betweeen them but at this moment only processor 1 can compute that
21041 // info., since it is the only one that currently has that info.
21042
21043 // Store the new local-halo elements of each processor, the HALOED
21044 // elements are also stored in the container, only needs to INVERT
21045 // the indexes. For example, the HALO elements of processor 2 with
21046 // processor 3 are stored in new_local_halo_element_pt[2][3], and
21047 // the HALOED elements of processor 2 with processor 3 are stored in
21048 // new_local_halo_element_pt[3][2]. Notice that these are also the
21049 // halo elements of processor 3 with 2
21050
21051 // How to identify the new local halo/haloed element: 1) Loop over
21052 // the element; 2) Only work with nonhalo elements; 3) If the
21053 // element is not assigned to the current processor (iproc) then
21054 // check; 4) Is one of its nodes assiociated to the iproc processor?
21055 // 5) If yes the element is a halo in the iproc processor whose
21056 // nonhalo counter part (haloed) lives in the domain assigned to the
21057 // element
21058 Vector<Vector<Vector<FiniteElement*>>> new_local_halo_element_pt(nproc);
21059
21060 // Loop over the processors
21061 for (unsigned iproc = 0; iproc < nproc; iproc++)
21062 {
21063 // Resize the container
21064 new_local_halo_element_pt[iproc].resize(nproc);
21065
21066 // Boolean to know which elements have been already added to the
21067 // new local halo scheme in "iproc"
21068 Vector<std::map<FiniteElement*, bool>> new_local_halo_already_added(
21069 nproc);
21070
21071 // Go through all the elements and identify the new local halo
21072 // elements of "iproc"
21073 unsigned nh_count5 = 0;
21074 for (unsigned e = 0; e < nelement_before_load_balance; e++)
21075 {
21076 // Get the element
21077 FiniteElement* ele_pt = this->finite_element_pt(e);
21078 // Only work with nonhalo elements
21079 if (!(ele_pt->is_halo()))
21080 {
21081 // Get the domain to which the current element is associated
21082 const unsigned ele_domain =
21083 target_domain_for_local_non_halo_element[nh_count5++];
21084 // If the current element is not associated to the "iproc"
21085 // processor then it could be a halo element
21086 if (ele_domain != iproc)
21087 {
21088 // Get the number of nodes
21089 const unsigned nnodes = ele_pt->nnode();
21090 // Loop over the nodes
21091 for (unsigned j = 0; j < nnodes; j++)
21092 {
21093 Node* node_pt = ele_pt->node_pt(j);
21094 // Check if the node is associated with the current
21095 // "iproc" processor
21096 std::set<unsigned>::iterator it =
21097 processors_associated_with_data_before_load_balance[node_pt]
21098 .find(iproc);
21099 // If it is found then the element is a halo-element
21100 if (it !=
21101 processors_associated_with_data_before_load_balance[node_pt]
21102 .end())
21103 {
21104 // Add the element as new local-halo element with the
21105 // "ele_domain" processor. The non-halo counterpart will
21106 // be located on "ele_domain" processor after sending
21107 // elements across
21108 if (!new_local_halo_already_added[ele_domain][ele_pt])
21109 {
21110 // The element is a halo element on "iproc" with
21111 // "ele_domain"
21112 new_local_halo_element_pt[iproc][ele_domain].push_back(
21113 ele_pt);
21114 // Mark as done
21115 new_local_halo_already_added[ele_domain][ele_pt] = true;
21116 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21117 } // One of the nodes lies on an element on the current
21118 // "iproc" processor
21119 } // for (j < nnodes)
21120 } // if (ele_domain != iproc)
21121 } // if (!(ele_pt->is_halo()))
21122 } // for (e < nelement_before_load_balance)
21123
21124 // Now do the same with the halo elements, we need to find those
21125 // halo elements that continue being halo elements but possibly
21126 // with/on another processor. The pair of processors where a
21127 // possible shared boundary is created needs to be notified.
21128
21129 // Example
21130 //
21131 // ---------------* *---------------
21132 // | |* *| |
21133 // | |* *| |
21134 // | New domain |* *| New domain | * Mark the position
21135 // | proc 1 |* *| proc 3 | of halo elements
21136 // | |* *| |
21137 // | |* *| |
21138 // ---------------* *---------------
21139 // Proc 1 Proc 2
21140
21141 // Processor 1: The halo elements on processor 1 continue being halo ON
21142 // PROCESSOR 1, but now WITH PROCESSOR 3
21143
21144 // Processor 2: The halo elements on processor 2 continue being
21145 // halo BUT now ON PROCESSOR 3 WITH PROCESSOR 1
21146
21147 // The current processor (my_rank) also needs to consider the halo
21148 // elements that will be halo elements of other processor with
21149 // another processor. The case of processor 2
21150
21151 // Loop over all the halo elements in the current processor and
21152 // check if they will be halo with the "iproc" processor
21153 for (unsigned jproc = 0; jproc < nproc; jproc++)
21154 {
21155 // There are no halo elements with myself (the old halo elements
21156 // were halo in the "my_rank" processor)
21157 if (jproc != my_rank)
21158 {
21159 // Get the number of halo elements with the "jproc" processor
21160 const unsigned n_halo_ele_jproc = this->nroot_halo_element(jproc);
21161 // Get the halo elements with the "jproc" processor
21162 Vector<GeneralisedElement*> halo_element_pt_jproc =
21163 this->root_halo_element_pt(jproc);
21164 // ... and check if any of those elements is a new halo
21165 // element with the "iproc" processor
21166 for (unsigned jh = 0; jh < n_halo_ele_jproc; jh++)
21167 {
21168 // Get the new domain for the halo element
21169 const unsigned ele_domain = new_domains_halo_elements[jproc][jh];
21170
21171 // If the current element is not associated to the "iproc"
21172 // processor then it could be a halo element on "iproc" with
21173 // "ele_domain".
21174
21175 // NOTE OUTDATE: Check if the halo element is going to be
21176 // sent to this processor (my_rank), if that is the case
21177 // then we don't need to add it to the set of new halo
21178 // elements with any other processor since any possible
21179 // shared boundary will be created when checking for the
21180 // intersection of the sent and received elements
21181
21182 // if (ele_domain != iproc && ele_domain != my_rank)
21183
21184 // NOTE UPDATE: Only check if the halo element is not going
21185 // to be part of the iproc processor, not required to avoid
21186 // those halo elements whose domain is the current rank
21187 // (my_rank). When the shared boundaries are computed, these
21188 // last elements can not create a shared boundary since no
21189 // haloed elements (that shared an edge) are found for
21190 // them. By considering also those halo elements whose new
21191 // domain is the current one (commenting "ele_domain !=
21192 // my_rank") the current processor can compute shared
21193 // boundaries with the iproc processor with help of its old
21194 // halo elements but that will become nonhalo elements, in
21195 // fact they will become haloed elements The halo element is
21196 // not sent to the "element_domain" processor and is not
21197 // passed to the array used to create the new shared
21198 // boundaries "new_shared_boundary_element_pt" because of
21199 // its halo condition
21200 if (ele_domain != iproc)
21201 {
21202 // Get the finite element
21203 FiniteElement* ele_pt =
21204 dynamic_cast<FiniteElement*>(halo_element_pt_jproc[jh]);
21205 // Get the number of nodes on the halo element
21206 const unsigned nnodes = ele_pt->nnode();
21207 // Loop over the nodes
21208 for (unsigned j = 0; j < nnodes; j++)
21209 {
21210 // Get each node of the halo element
21211 Node* node_pt = ele_pt->node_pt(j);
21212
21213 // Check if the node is associated with the "iproc"
21214 // processor
21215 std::set<unsigned>::iterator it =
21216 processors_associated_with_data_before_load_balance[node_pt]
21217 .find(iproc);
21218 // If it is found then the element is a halo-element
21219 if (it !=
21220 processors_associated_with_data_before_load_balance[node_pt]
21221 .end())
21222 {
21223 // Add the element as new local-halo element with
21224 // the "ele_domain" processor. The non-halo
21225 // counterpart will be located on "ele_domain"
21226 // processor. Because this is a old-halo element it
21227 // will not be sent to the "element_domain" processor
21228 if (!new_local_halo_already_added[ele_domain][ele_pt])
21229 {
21230 // The element is a halo element on "iproc" with
21231 // "ele_domain"
21232 new_local_halo_element_pt[iproc][ele_domain].push_back(
21233 ele_pt);
21234 new_local_halo_already_added[ele_domain][ele_pt] = true;
21235
21236 // Break the for of the nodes, the element has been
21237 // already added to the new_local_halo_element_pt
21238 // structure
21239 break;
21240
21241 } // if (!new_local_halo_already_added[ele_domain][ele_pt])
21242
21243 } // One of the nodes lies on an element belonging to
21244 // "iproc" processor
21245
21246 } // for (j < nnodes)
21247
21248 } // if (ele_domain != iproc)
21249
21250 } // for (jh < n_halo_ele_jproc)
21251
21252 } // if (jproc != my_rank) // The old halo elements are halo
21253 // with other processors except with "my_rank"
21254
21255 } // for (jproc < nproc): This is the one that goes for the halo
21256 // elements in the current processor to find the new halo
21257 // elements
21258
21259 } // for (iproc < nproc)
21260
21261 // Get the time to compute new local halo elements within all
21262 // processors
21263 if (Print_timings_level_load_balance > 1)
21264 {
21266 << "CPU for computing new local halo elements (load balance) [4]: "
21267 << TimingHelpers::timer() - tt_start_compute_new_local_halo_elements
21268 << std::endl;
21269 }
21270
21271 // =====================================================================
21272 // END: COMPUTE THE NEW LOCAL HALO ELEMENTS OF ALL PROCESSORS IN THE
21273 // CURRENT PROCESSOR
21274 // ----- FOR NONHALO ELEMENTS AND FOR HALO ELEMENTS
21275 // =====================================================================
21276
21277 // =====================================================================
21278 // BEGIN: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE
21279 // FACE ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART
21280 // OF THE NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE
21281 // MARKED AS HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY
21282 // ELEMENTS IN THE RECEIVED PROCESSOR
21283 // =====================================================================
21284
21285 // Get the time to compute new local shared boundary elements
21286 double tt_start_compute_new_local_shd_bnd_ele = 0.0;
21287 if (Print_timings_level_load_balance > 1)
21288 {
21289 tt_start_compute_new_local_shd_bnd_ele = TimingHelpers::timer();
21290 }
21291
21292 // Store the new local-shared boundary elements and the face indexes
21293 // The halo elements and halo face indexes
21295 new_local_halo_shared_boundary_element_pt(nproc);
21297 new_local_halo_shared_boundary_element_face_index(nproc);
21298
21299 // Allocate enough memory for the containers
21300 for (unsigned iproc = 0; iproc < nproc; iproc++)
21301 {
21302 new_local_halo_shared_boundary_element_pt[iproc].resize(nproc);
21303 new_local_halo_shared_boundary_element_face_index[iproc].resize(nproc);
21304 } // for (iproc < nproc)
21305
21306 // Get the elements that create the new local-halo-shared
21307 // boundaries, mark them and identify the face that lies on the
21308 // shared boundary. The new local-halo-shared boundary elements are
21309 // actually a sub-set of the halo elements of each processor with in
21310 // each processor
21311 for (unsigned iproc = 0; iproc < nproc; iproc++)
21312 {
21313 // Star from jproc = iproc + 1 to avoid double creation of shared
21314 // boundary elements, any shared boundary element identified
21315 // between processor "iproc" and "jproc" is also established as
21316 // shared boundary element between processor "jproc" and "iproc"
21317 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
21318 {
21319 this->get_shared_boundary_elements_and_face_indexes(
21320 new_local_halo_element_pt[iproc][jproc],
21321 new_local_halo_element_pt[jproc][iproc],
21322 new_local_halo_shared_boundary_element_pt[iproc][jproc],
21323 new_local_halo_shared_boundary_element_face_index[iproc][jproc],
21324 new_local_halo_shared_boundary_element_pt[jproc][iproc],
21325 new_local_halo_shared_boundary_element_face_index[jproc][iproc]);
21326 } // for (jproc < nproc)
21327 } // for (iproc < nproc)
21328
21329 // The time to compute new local shared boundary elements
21330 if (Print_timings_level_load_balance > 1)
21331 {
21332 oomph_info << "CPU for computing new local shared boundary elements "
21333 "(load balance) [5]: "
21335 tt_start_compute_new_local_shd_bnd_ele
21336 << std::endl;
21337 }
21338
21339 // =====================================================================
21340 // END: COMPUTE THE NEW LOCAL SHARED BOUNDARY ELEMENTS AND THE FACE
21341 // ELEMENTS. THE SUBSET OF THE ELEMENTS TO SENT THAT ARE PART OF THE
21342 // NEW LOCAL SHARED BOUNDARY ELEMENTS ARE IDENTIFIED TO BE MARKED AS
21343 // HALOED ELEMENTS AND BELONGING TO THE SHARED BOUNDARY ELEMENTS IN
21344 // THE RECEIVED PROCESSOR
21345 // =====================================================================
21346
21347 // =====================================================================
21348 // BEGIN: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21349 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21350 // =====================================================================
21351
21352 // Get the time to send the elements to their new processor in
21353 // charge
21354 double tt_start_send_elements_to_other_processors = 0.0;
21355 if (Print_timings_level_load_balance > 1)
21356 {
21357 tt_start_send_elements_to_other_processors = TimingHelpers::timer();
21358 }
21359
21360 // Sort the nodes on shared boundaries so that they have the same
21361 // order on all the shared boundaries, this is required to know the
21362 // possible shared nodes among processors
21363 this->sort_nodes_on_shared_boundaries();
21364
21365 // Store the received elements from each processor
21366 Vector<Vector<FiniteElement*>> received_elements_pt(nproc);
21367
21368 // The haloed elements and haloed face indexes, these store the
21369 // haloed elements received from "iproc" but that are haloed with
21370 // "jproc". The elements are received from "iproc" which was the
21371 // processor that computed the haloed relation of the "my_rank"
21372 // processor with "jproc"
21374 new_received_haloed_shared_boundary_element_pt(nproc);
21376 new_received_haloed_shared_boundary_element_face_index(nproc);
21377
21378 // Container where to store the nodes on shared boundaries not
21379 // associated with the processor that receives the elements/nodes
21380 // other_proc_shd_bnd_node_pt[iproc][jproc][shd_bnd_id][index]
21382 other_proc_shd_bnd_node_pt(nproc);
21383 // Resize the container
21384 for (unsigned iproc = 0; iproc < nproc; iproc++)
21385 {
21386 // Resize the container
21387 other_proc_shd_bnd_node_pt[iproc].resize(nproc);
21388 for (unsigned jproc = 0; jproc < nproc; jproc++)
21389 {
21390 // Get the number of shared boundaries (OLD shared boundaries)
21391 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
21392 const unsigned final_shd_bnd_id = this->final_shared_boundary_id();
21393 const unsigned n_shared_bound = final_shd_bnd_id - initial_shd_bnd_id;
21394 other_proc_shd_bnd_node_pt[iproc][jproc].resize(n_shared_bound);
21395 } // for (jproc < nproc)
21396
21397 } // for (iproc < nproc)
21398
21399 // Store the global node names
21400 // global_node_name[x][ ][ ] Global node number
21401 // global_node_name[ ][x][ ] Global node names
21402 // global_node_name[ ][ ][x] Global node info.
21403 Vector<Vector<Vector<unsigned>>> global_node_names;
21404
21405 // Creates a map between the node name and the index of the global
21406 // node so we can access all its node names
21407 std::map<Vector<unsigned>, unsigned> node_name_to_global_index;
21408
21409 // Store the global shared nodes pointers
21410 Vector<Node*> global_shared_node_pt;
21411
21412 // Compute all the names of the nodes and fill in the
21413 // "other_proc_shd_bnd_node_pt" structure with the nodes that live
21414 // on this processor (my_rank) by looking over all their names
21415 compute_global_node_names_and_shared_nodes(other_proc_shd_bnd_node_pt,
21416 global_node_names,
21417 node_name_to_global_index,
21418 global_shared_node_pt);
21419
21420 // From the elements received from each processor, store the haloed
21421 // information of the element, it means, the processor with which it
21422 // is haloed and the haloed index with that processor
21424 received_old_haloed_element_pt(nproc);
21425 // [x][][] : The receiver processor (the original processor)
21426 // [][x][] : The processor with which the receiver processor has
21427 // haloed elements
21428 // [][][x]: The haloed element number
21429
21430 // Resize the container
21431 for (unsigned iproc = 0; iproc < nproc; iproc++)
21432 {
21433 received_old_haloed_element_pt[iproc].resize(nproc);
21434 } // for (iproc < nproc)
21435
21436 // Go through all processors and send the corresponding elements to
21437 // each one
21438 for (unsigned iproc = 0; iproc < nproc; iproc++)
21439 {
21440 if (iproc != my_rank)
21441 {
21442 // -----------------------------------------------------------
21443 // Send (package) information of the elements
21444 // -----------------------------------------------------------
21445
21446 // Keep track of the currently sent elements
21447 Vector<FiniteElement*> currently_sent_elements;
21448 // Keep track of the currently sent nodes to the iproc processor
21449 Vector<Node*> currently_sent_nodes;
21450
21451 // Clear send and receive buffers
21452 Flat_packed_unsigneds.clear();
21453 Flat_packed_doubles.clear();
21454#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21456#endif
21457
21458 // Get the number of elements to send to iproc processor
21459 const unsigned nelements_to_send = elements_to_send_pt[iproc].size();
21460
21461 // The very first data of the flat package sent to processor
21462 // iproc is the number of elements that will be sent, this data
21463 // is used by the receiver processor to loop over the number of
21464 // expected elements to receive
21465 Flat_packed_unsigneds.push_back(nelements_to_send);
21466#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21467 std::stringstream junk;
21468 junk << "Number of elements to send from processor " << my_rank
21469 << " to processor " << iproc << ": (" << nelements_to_send << ")";
21470 Flat_packed_unsigneds_string.push_back(junk.str());
21471#endif
21472
21473 // Loop over the elements to sent
21474 for (unsigned e = 0; e < nelements_to_send; e++)
21475 {
21476 // Get the element to send
21477 FiniteElement* send_ele_pt = elements_to_send_pt[iproc][e];
21478
21479 // Get the current number of sent elements
21480 const unsigned ncurrently_sent_elements =
21481 currently_sent_elements.size();
21482
21483 // Try to add the element
21484 const unsigned index_ele = try_to_add_element_pt_load_balance(
21485 currently_sent_elements, send_ele_pt);
21486
21487 // Element needs to be added
21488 if (index_ele == ncurrently_sent_elements)
21489 {
21490 Flat_packed_unsigneds.push_back(1);
21491#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21493 "Element needs to be constructed");
21494#endif
21495
21496 // Get required info. related with the element
21497 get_required_elemental_information_load_balance_helper(
21498 iproc, f_haloed_element_pt, send_ele_pt);
21499
21500 // Get the number of nodes in the element
21501 const unsigned nnodes = send_ele_pt->nnode();
21502
21503 // Loop over the nodes in the element
21504 for (unsigned j = 0; j < nnodes; j++)
21505 {
21506 Node* node_pt = send_ele_pt->node_pt(j);
21507
21508 // Package the info. of the nodes
21509 add_node_load_balance_helper(iproc, // The destination process
21510 f_halo_element_pt,
21511 currently_sent_nodes,
21512 node_pt);
21513
21514 } // for (j < nnodes)
21515
21516 } // if (index_ele == ncurrently_sent_elements)
21517 else
21518 {
21519 Flat_packed_unsigneds.push_back(0);
21520#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21521 Flat_packed_unsigneds_string.push_back("Element already exists");
21522#endif
21523 Flat_packed_unsigneds.push_back(index_ele);
21524#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21525 Flat_packed_unsigneds_string.push_back("Index of existing element");
21526#endif
21527 } // else if (index_ele == ncurrently_sent_elements)
21528
21529 } // for (e < nelements_to_send)
21530
21531 // After storing the info. of the elements identify the indexes
21532 // of the "new_local_halo_shared_boundary_elements" in the
21533 // "currently_send_elements" vector, these elements will be
21534 // identified as "new_received_haloed_shared_boundary_elements"
21535 // on the "receiver" processor
21536
21537 // Each processor has information of every other processor so we
21538 // need to send all the corresponding info. to the other
21539 // processors. Processor 1 may have information of the relation
21540 // (halo elements) between processor 3 and 4 say, so processor 1
21541 // needs to let know processor 3 and 4 what this relation is
21542 // (which are the shared-elements among these processors)
21543
21544 for (unsigned jproc = 0; jproc < nproc; jproc++)
21545 {
21546 // Get the number of new local-halo shared boundary elements
21547 // between processor "jproc" and "iproc" (we invert the index
21548 // since we really want the haloed elements, those elements
21549 // that we have just sent)
21550 const unsigned njproc_iproc_new_local_halo_shared_boundary_ele =
21551 new_local_halo_shared_boundary_element_pt[jproc][iproc].size();
21552
21553 // The vector with the info. of the indexes
21554 Vector<unsigned> new_local_halo_shared_boundary_ele_index;
21555
21556 // The number of found shared boundary elements in the sent
21557 // container (only consider the nonhalo elements)
21558 unsigned nfound_new_local_halo_shared_bound_ele_index = 0;
21559 // The number of nonhalo elements in the new local halo shared
21560 // boundary elements
21561 unsigned nnon_halo_new_local_halo_shared_bound_ele = 0;
21562
21563 // Loop over the local halo shared boundary elements between
21564 // processor jproc and iproc
21565 for (unsigned e = 0;
21566 e < njproc_iproc_new_local_halo_shared_boundary_ele;
21567 e++)
21568 {
21569 // Get the shared boundary element
21570 FiniteElement* shared_ele_pt =
21571 new_local_halo_shared_boundary_element_pt[jproc][iproc][e];
21572
21573 // Only consider the nonhalo elements since the halo
21574 // elements were no considered for sending
21575 if (!shared_ele_pt->is_halo())
21576 {
21577 nnon_halo_new_local_halo_shared_bound_ele++;
21578
21579 // Now find the index on the currently sent elements
21580
21581 // Get the current number of sent elements
21582 const unsigned ncurrently_sent_elements =
21583 currently_sent_elements.size();
21584 // Loop over the sent elements
21585 for (unsigned ics = 0; ics < ncurrently_sent_elements; ics++)
21586 {
21587 FiniteElement* currently_sent_ele_pt =
21588 currently_sent_elements[ics];
21589
21590 // Is this the element?
21591 if (currently_sent_ele_pt == shared_ele_pt)
21592 {
21593 // Store the index on the sent elements of the local
21594 // halo shared boundary element
21595 new_local_halo_shared_boundary_ele_index.push_back(ics);
21596 // Increase the number of found new local halo shared
21597 // bound element index
21598 nfound_new_local_halo_shared_bound_ele_index++;
21599 // We have found it, no need to further search
21600 break;
21601 } // if (currently_sent_ele_pt == shared_ele_pt)
21602
21603 } // for (ics < ncurrently_sent_elements)
21604
21605 } // if (!shared_ele_pt->is_halo())
21606
21607 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21608
21609#ifdef PARANOID
21610 if (nfound_new_local_halo_shared_bound_ele_index !=
21611 nnon_halo_new_local_halo_shared_bound_ele)
21612 {
21613 std::ostringstream error_message;
21614 error_message << "Was only possible to identify ("
21615 << nfound_new_local_halo_shared_bound_ele_index
21616 << ") of ("
21617 << nnon_halo_new_local_halo_shared_bound_ele
21618 << ") shared "
21619 << "elements between\nprocessor (" << iproc
21620 << ") and (" << jproc << ") "
21621 << "when sending elements to processor (" << iproc
21622 << ")\n\n";
21623 throw OomphLibError(error_message.str(),
21624 OOMPH_CURRENT_FUNCTION,
21625 OOMPH_EXCEPTION_LOCATION);
21626 }
21627#endif
21628
21629 // Send a flag for synchronisation issues
21630 Flat_packed_unsigneds.push_back(9999);
21631#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21632 std::stringstream junk;
21633 junk << "Flag for synchronisation 9999";
21634 Flat_packed_unsigneds_string.push_back(junk.str());
21635#endif
21636
21637 // Send the number of nonhalo new local-shared boundary
21638 // elements of processor "iproc" with processor "jproc"
21639 Flat_packed_unsigneds.push_back(
21640 nnon_halo_new_local_halo_shared_bound_ele);
21641#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21642 std::stringstream junk2;
21643 junk2 << "Number of new local halo shared boundary elements "
21644 << nnon_halo_new_local_halo_shared_bound_ele;
21645 Flat_packed_unsigneds_string.push_back(junk2.str());
21646#endif
21647
21648 // Send the indexes and the face indexes of the shared
21649 // boundary elements
21650 unsigned counter_nonhalo_sent = 0;
21651 // Loop over the local halo shared boundary elements between
21652 // processor jproc and iproc
21653 for (unsigned e = 0;
21654 e < njproc_iproc_new_local_halo_shared_boundary_ele;
21655 e++)
21656 {
21657 // Get the shared boundary element
21658 FiniteElement* shared_ele_pt =
21659 new_local_halo_shared_boundary_element_pt[jproc][iproc][e];
21660
21661 // Only consider the nonhalo elements since the halo
21662 // elements were no considered for sending
21663 if (!shared_ele_pt->is_halo())
21664 {
21665 // Get the index on the sent elements of the current
21666 // nonhalo shared boundary element
21667 const unsigned ele_index =
21668 new_local_halo_shared_boundary_ele_index
21669 [counter_nonhalo_sent++];
21670 // ... and get the face index
21671 const unsigned face_index =
21672 new_local_halo_shared_boundary_element_face_index[jproc][iproc]
21673 [e];
21674
21675 // Send the index on the sent elements of the new local
21676 // halo shared boundary element
21677 Flat_packed_unsigneds.push_back(ele_index);
21678#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21679 std::stringstream junk;
21680 junk << "The index of the halo shared boundary element "
21681 << ele_index;
21682 Flat_packed_unsigneds_string.push_back(junk.str());
21683#endif
21684
21685 // Send the face index of the new local halo shared boundary
21686 // element
21687 Flat_packed_unsigneds.push_back(face_index);
21688#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21689 std::stringstream junk2;
21690 junk2 << "The face index of the halo shared boundary element "
21691 << face_index;
21692 Flat_packed_unsigneds_string.push_back(junk2.str());
21693#endif
21694
21695 } // if (!shared_ele_pt->is_halo())
21696
21697 } // for (e < niproc_new_local_halo_shared_boundary_ele)
21698
21699 } // for (jproc < nproc)
21700
21701 // ----------------------------------------------------------
21702 // Send the info. perform the communications
21703 // ----------------------------------------------------------
21704 // Processor to which send the info.
21705 int send_proc = static_cast<int>(iproc);
21706 // Processor from which receive the info.
21707 int recv_proc = static_cast<int>(iproc);
21708 send_and_receive_elements_nodes_info(send_proc, recv_proc);
21709
21710 // ----------------------------------------------------------
21711 // Receive (unpackage) the info of the elements
21712 // ----------------------------------------------------------
21713
21714 // Keep track of the currently created elements
21715 Vector<FiniteElement*> currently_created_elements;
21716 // Keep track of the currently created nodes
21717 Vector<Node*> currently_created_nodes;
21718
21719 // Reset the counters
21722
21723#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
21725 << " Number of elements need to be constructed "
21727 << std::endl;
21728#endif
21729
21730 // Read the number of elements that need to be created
21731 const unsigned nelements_to_create =
21733
21734 for (unsigned e = 0; e < nelements_to_create; e++)
21735 {
21736 // Create the element from received info. of "iproc"
21737 // processor on the current processor
21738 create_element_load_balance_helper(iproc,
21739 f_haloed_element_pt,
21740 received_old_haloed_element_pt,
21741 currently_created_elements,
21742 currently_created_nodes,
21743 other_proc_shd_bnd_node_pt,
21744 global_node_names,
21745 node_name_to_global_index,
21746 global_shared_node_pt);
21747 }
21748
21749 // Copy the received elements from "iproc" processor
21750
21751 // Number of received elements
21752 const unsigned nreceived_elements = currently_created_elements.size();
21753 received_elements_pt[iproc].resize(nreceived_elements);
21754 for (unsigned e = 0; e < nreceived_elements; e++)
21755 {
21756 received_elements_pt[iproc][e] = currently_created_elements[e];
21757 }
21758
21759 // Go for the haloed elements received from processor "iproc"
21760 // but haloed with "jproc"
21761
21762 // Allocate memory for the containers
21763 new_received_haloed_shared_boundary_element_pt[iproc].resize(nproc);
21764 new_received_haloed_shared_boundary_element_face_index[iproc].resize(
21765 nproc);
21766
21767 // Loop over the processors
21768 for (unsigned jproc = 0; jproc < nproc; jproc++)
21769 {
21770 // Read the synchronisation flag
21771 const unsigned synchronisation_flag =
21773
21774 if (synchronisation_flag != 9999)
21775 {
21776 std::ostringstream error_message;
21777 error_message << "The synchronisation flag was not read, the\n"
21778 << "information sent between processor (" << my_rank
21779 << ") "
21780 << "and (" << iproc
21781 << ")\nis no longer synchronised\n\n";
21782 throw OomphLibError(error_message.str(),
21783 OOMPH_CURRENT_FUNCTION,
21784 OOMPH_EXCEPTION_LOCATION);
21785 }
21786
21787 // Read the number of elements that will be part of the new
21788 // received haloed shared boundary elements received from "iproc"
21789 // and haloed with "jproc"
21790 const unsigned niproc_jproc_new_received_haloed_shared_boundary_ele =
21792
21793 // Loop over the new received haloed shared boundary elements
21794 for (unsigned e = 0;
21795 e < niproc_jproc_new_received_haloed_shared_boundary_ele;
21796 e++)
21797 {
21798 // Read the index of the new received haloed shared boundary
21799 // ele with "jproc"
21800 const unsigned ele_index =
21802 // Read the face index for the new received haloed shared
21803 // boundary element
21804 const unsigned face_index =
21806
21807 // Get the element
21808 FiniteElement* shared_ele_pt =
21809 currently_created_elements[ele_index];
21810
21811 // Add the element to the new received-haloed shared
21812 // boundary elements. Received from "iproc" but haloed with
21813 // "jproc" processor
21814 new_received_haloed_shared_boundary_element_pt[iproc][jproc]
21815 .push_back(shared_ele_pt);
21816 // Store the face index
21817 new_received_haloed_shared_boundary_element_face_index[iproc][jproc]
21818 .push_back(face_index);
21819
21820 } // for (e < niproc_jproc_read_new_local_shared_boundary_ele)
21821
21822 } // for (jproc < nproc)
21823
21824 } // if (iproc != my_rank)
21825
21826 } // for (iproc < nproc)
21827
21828 // The time to send the elements to their new processor in charge
21829 if (Print_timings_level_load_balance > 1)
21830 {
21831 oomph_info << "CPU for sending elements to their new processors (load "
21832 "balance) [6]: "
21834 tt_start_send_elements_to_other_processors
21835 << std::endl;
21836 }
21837
21838 // =====================================================================
21839 // END: SEND THE ELEMENTS AND IDENTIFY THOSE THAT ARE PART OF THE
21840 // SHARED BOUNDARIES AND HALOED WITH OTHER PROCESSORS
21841 // =====================================================================
21842
21843 // =====================================================================
21844 // BEGIN: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21845 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21846 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21847 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21848 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21849 // ELEMENTS)
21850 // =====================================================================
21851
21852 // Get the time to compute any additional shared boundary
21853 double tt_start_compute_additional_shared_boundaries = 0.0;
21854 if (Print_timings_level_load_balance > 1)
21855 {
21856 tt_start_compute_additional_shared_boundaries = TimingHelpers::timer();
21857 }
21858
21859 // Store any additional elements that may create a shared boundary,
21860 // after sending elements from one to other processor check for any
21861 // new possible shared boundaries
21862 Vector<Vector<FiniteElement*>> tmp_group1_shared_boundary_element_pt(nproc);
21863 Vector<Vector<unsigned>> tmp_group1_shared_boundary_element_face_index(
21864 nproc);
21865 Vector<Vector<FiniteElement*>> tmp_group2_shared_boundary_element_pt(nproc);
21866 Vector<Vector<unsigned>> tmp_group2_shared_boundary_element_face_index(
21867 nproc);
21868
21869 // Compute any additional shared boundaries by checking the
21870 // intersection between the received elements from each processor
21871 // and the elements just sent to that processor, the lowest
21872 // processors number loops over its received elements and the
21873 // highest loops over its sent elements (halo elements that have
21874 // become part of the domain now can create shared boundaries with
21875 // other processor)
21876
21877 // Note: These additional shared boundaries may be created by the
21878 // elements that previously were halo but now have become part of
21879 // the processor (the received elements), and the elements that were
21880 // previously part of the processor but now have become halo (a
21881 // subset of the sent-elements)
21882
21883 // Then these new shared boundaries come from the intersection of
21884 // the new-haloed elements (received elements) and the new-halo
21885 // elements (sent elements). These could be computed previously (in
21886 // the computing of the local new-halo and local new-haloed elements
21887 // usign the info. of the new domains for the old halo elements),
21888 // however, it was decided to perform the computation here in order to
21889 // avoid the identification of the old halo element that was part of a
21890 // shared boundary in the set of just received elements
21891 for (unsigned iproc = 0; iproc < nproc; iproc++)
21892 {
21893 if (my_rank < iproc)
21894 {
21895 // Lowest processor loops over the received elements
21896 this->get_shared_boundary_elements_and_face_indexes(
21897 received_elements_pt[iproc],
21898 elements_to_send_pt[iproc],
21899 tmp_group1_shared_boundary_element_pt[iproc],
21900 tmp_group1_shared_boundary_element_face_index[iproc],
21901 tmp_group2_shared_boundary_element_pt[iproc],
21902 tmp_group2_shared_boundary_element_face_index[iproc]);
21903
21904 } // if (my_rank < iproc)
21905 else if (my_rank > iproc)
21906 {
21907 // Highest processor loops over the sent elements
21908 this->get_shared_boundary_elements_and_face_indexes(
21909 elements_to_send_pt[iproc],
21910 received_elements_pt[iproc],
21911 tmp_group1_shared_boundary_element_pt[iproc],
21912 tmp_group1_shared_boundary_element_face_index[iproc],
21913 tmp_group2_shared_boundary_element_pt[iproc],
21914 tmp_group2_shared_boundary_element_face_index[iproc]);
21915
21916 } // else if (my_rank > iproc)
21917
21918 } // for (iproc < nproc)
21919
21920 // The time to compute any additional shared boundary
21921 if (Print_timings_level_load_balance > 1)
21922 {
21924 << "CPU for computing additional shared boundaries (load balance) [7]: "
21926 tt_start_compute_additional_shared_boundaries
21927 << std::endl;
21928 }
21929
21930 // =====================================================================
21931 // END: GET ANY ADDITIONAL SHARED BOUNDARY BY THE INTERSECTION OF
21932 // THE ELEMENTS SENT TO PROCESSOR "IPROC" AND THE ELEMENTS RECEIVED
21933 // FROM PROCESSOR "IPROC". IF ANY NEW SHARED BOUNDARY IS FOUND, IT
21934 // IS CREATED BY THE OLD HALO ELEMENTS (RECEIVED ELEMENTS) THAT HAVE
21935 // NOW BECOME PART OF THE DOMAIN AND THE OLD HALOED ELEMENTS (SENT
21936 // ELEMENTS)
21937 // =====================================================================
21938
21939 // =====================================================================
21940 // BEGIN: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
21941 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
21942 // =====================================================================
21943
21944 // Get the time to sort shared boundaries
21945 double tt_start_sort_shared_boundaries = 0.0;
21946 if (Print_timings_level_load_balance > 1)
21947 {
21948 tt_start_sort_shared_boundaries = TimingHelpers::timer();
21949 }
21950
21951 // Once computed the elements that create the shared boundaries,
21952 // sort them so that the shared boundaries are created at the same
21953 // order in both processors that define the shared boundary
21954
21955 // The order is like this
21956
21957 // Lowest processors
21958 // 1) Shared boundary elements received from processors (local in
21959 // other processors)
21960 // 2) Local shared boundary elements (do not include halo elements)
21961 // 3) Shared boundary elements by intersection (already sorted)
21962
21963 // Highest processors
21964 // 1) Local shared boundary elements (do not include halo elements)
21965 // 2) Shared boundary elements received from processors (local in
21966 // other processors)
21967 // 3) Shared boundary elements by intersection (already sorted)
21968
21969 Vector<Vector<FiniteElement*>> new_shared_boundary_element_pt(nproc);
21970 Vector<Vector<unsigned>> new_shared_boundary_element_face_index(nproc);
21971 for (unsigned iproc = 0; iproc < nproc; iproc++)
21972 {
21973 // Lower processor
21974 if (my_rank < iproc)
21975 {
21976 // Copy the elements received from processor "jproc" but that
21977 // are haloed with "iproc" processor
21978 for (unsigned jproc = 0; jproc < nproc; jproc++)
21979 {
21980 // Can not receive elements from itself
21981 if (jproc != my_rank)
21982 {
21983 // Get the number of elements to copy from received processors
21984 const unsigned nrecvd_haloed_shared_bound_ele_jproc_iproc =
21985 new_received_haloed_shared_boundary_element_pt[jproc][iproc]
21986 .size();
21987 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
21988 e++)
21989 {
21990 // Get the element
21991 FiniteElement* ele_pt =
21992 new_received_haloed_shared_boundary_element_pt[jproc][iproc][e];
21993 // Get the face index
21994 const unsigned face_index =
21995 new_received_haloed_shared_boundary_element_face_index[jproc]
21996 [iproc]
21997 [e];
21998
21999 // Add the elements to the containers
22000 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22001 new_shared_boundary_element_face_index[iproc].push_back(
22002 face_index);
22003
22004 } // for (e < nrecvd_haloed_shared_bound_ele_iproc_jproc)
22005
22006 } // if (jproc != my_rank)
22007
22008 } // for (jproc < nproc)
22009
22010 // Then the local shared haloed (invert the indexes to get the
22011 // haloed elements)
22012 const unsigned nlocal_haloed_shared_bound_ele_iproc_my_rank =
22013 new_local_halo_shared_boundary_element_pt[iproc][my_rank].size();
22014 for (unsigned e = 0; e < nlocal_haloed_shared_bound_ele_iproc_my_rank;
22015 e++)
22016 {
22017 // Get the element
22018 FiniteElement* ele_pt =
22019 new_local_halo_shared_boundary_element_pt[iproc][my_rank][e];
22020 // Get the face index
22021 const unsigned face_index =
22022 new_local_halo_shared_boundary_element_face_index[iproc][my_rank]
22023 [e];
22024
22025 // Only include the element if it is nonhalo (this may be an
22026 // old halo element that helped to indentify a shared boundary
22027 // with iproc)
22028 if (!ele_pt->is_halo())
22029 {
22030 // Add the elements to the containers
22031 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22032 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22033 } // if (!ele_pt->is_halo())
22034
22035 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22036
22037 // ... and finally any additional shared boundary elements from
22038 // tmp_group1
22039 const unsigned ntmp_group1_shared_bound_ele_iproc =
22040 tmp_group1_shared_boundary_element_pt[iproc].size();
22041 for (unsigned e = 0; e < ntmp_group1_shared_bound_ele_iproc; e++)
22042 {
22043 // Get the element
22044 FiniteElement* ele_pt =
22045 tmp_group1_shared_boundary_element_pt[iproc][e];
22046 // Get the face index
22047 const unsigned face_index =
22048 tmp_group1_shared_boundary_element_face_index[iproc][e];
22049
22050 // Add the elements to the containers
22051 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22052 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22053
22054 } // for (e < ntmp_group1_shared_bound_ele_iproc)
22055
22056 } // if (my_rank < iproc)
22057 // Highest processor
22058 else if (my_rank > iproc)
22059 {
22060 // Get the haloed elements first and then the elements received
22061 // from processor "jproc" but that are haloed with "iproc"
22062 // processor
22063
22064 // Get the number of elements to copy from local elements
22065 // (invert the indexes to get the haloed elements)
22066 const unsigned nlocal_haloed_shared_bound_ele_iproc_my_rank =
22067 new_local_halo_shared_boundary_element_pt[iproc][my_rank].size();
22068 for (unsigned e = 0; e < nlocal_haloed_shared_bound_ele_iproc_my_rank;
22069 e++)
22070 {
22071 // Get the element
22072 FiniteElement* ele_pt =
22073 new_local_halo_shared_boundary_element_pt[iproc][my_rank][e];
22074 // Get the face index
22075 const unsigned face_index =
22076 new_local_halo_shared_boundary_element_face_index[iproc][my_rank]
22077 [e];
22078
22079 // Only include the element if it is nonhalo (this may be an
22080 // old halo element that helped to indentify a shared boundary
22081 // with iproc)
22082 if (!ele_pt->is_halo())
22083 {
22084 // Add the elements to the containers
22085 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22086 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22087 } // if (!ele_pt->is_halo())
22088
22089 } // for (e < nlocal_haloed_shared_bound_ele_iproc_my_rank)
22090
22091 for (unsigned jproc = 0; jproc < nproc; jproc++)
22092 {
22093 // Can not receive elements from itself
22094 if (jproc != my_rank)
22095 {
22096 // Then the received shared elements from "jproc" but haloed
22097 // with "iproc"
22098 const unsigned nrecvd_haloed_shared_bound_ele_jproc_iproc =
22099 new_received_haloed_shared_boundary_element_pt[jproc][iproc]
22100 .size();
22101 for (unsigned e = 0; e < nrecvd_haloed_shared_bound_ele_jproc_iproc;
22102 e++)
22103 {
22104 // Get the element
22105 FiniteElement* ele_pt =
22106 new_received_haloed_shared_boundary_element_pt[jproc][iproc][e];
22107 // Get the face index
22108 const unsigned face_index =
22109 new_received_haloed_shared_boundary_element_face_index[jproc]
22110 [iproc]
22111 [e];
22112
22113 // Add the elements to the containers
22114 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22115 new_shared_boundary_element_face_index[iproc].push_back(
22116 face_index);
22117
22118 } // for (e < nrecvd_haloed_shared_bound_ele_iproc)
22119
22120 } // if (jproc != my_rank)
22121
22122 } // for (jproc < nproc)
22123
22124 // ... and finally any additional shared boundary elements from
22125 // tmp_group2
22126 const unsigned ntmp_group2_shared_bound_ele_iproc =
22127 tmp_group2_shared_boundary_element_pt[iproc].size();
22128 for (unsigned e = 0; e < ntmp_group2_shared_bound_ele_iproc; e++)
22129 {
22130 // Get the element
22131 FiniteElement* ele_pt =
22132 tmp_group2_shared_boundary_element_pt[iproc][e];
22133 // Get the face index
22134 const unsigned face_index =
22135 tmp_group2_shared_boundary_element_face_index[iproc][e];
22136
22137 // Add the elements to the containers
22138 new_shared_boundary_element_pt[iproc].push_back(ele_pt);
22139 new_shared_boundary_element_face_index[iproc].push_back(face_index);
22140
22141 } // for (e < ntmp_group2_shared_bound_ele_iproc)
22142
22143 } // else if (my_rank > iproc)
22144
22145 } // for (iproc < nproc)
22146
22147 // The time to sort shared boundaries
22148 if (Print_timings_level_load_balance > 1)
22149 {
22150 oomph_info << "CPU for sorting shared boundaries (load balance) [8]: "
22151 << TimingHelpers::timer() - tt_start_sort_shared_boundaries
22152 << std::endl;
22153 }
22154
22155 // =====================================================================
22156 // END: SORT THE SHARED BOUNDARIES SO THAT THEY ARE CREATED IN THE
22157 // SAME ORDER IN THE INVOLVED PROCESSORS (A PAIR OF PROCESSORS)
22158 // =====================================================================
22159
22160 // =====================================================================
22161 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22162 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22163 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22164 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22165 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22166 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22167 // THE ORIGINAL BOUNDARIES
22168 // =====================================================================
22169 // Finally, create the new shared boundaries
22170
22171 // Get the time to create the new shared boundaries
22172 double tt_start_create_new_shared_boundaries = 0.0;
22173 if (Print_timings_level_load_balance > 1)
22174 {
22175 tt_start_create_new_shared_boundaries = TimingHelpers::timer();
22176 }
22177
22178 // Compute the elements that will remain after deletion in the
22179 // curent processor. This is required to check if the new shared
22180 // boundaries crete a connection with any node of the elements in
22181 // the boundaries
22182
22183 // Try to use as much information as possible
22184
22185 // Storage for the elements in the processor
22186 std::set<FiniteElement*> element_in_processor_pt;
22187
22188 // Loop over the old elements, those before sending/received
22189 // elements to/from other processors
22190 unsigned nh_count6 = 0;
22191 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22192 {
22193 // Get the element
22194 FiniteElement* ele_pt = backed_up_ele_pt[e];
22195 // Only work with nonhalo elements
22196 if (!(ele_pt->is_halo()))
22197 {
22198 // Is the element part of the new domain
22199 if (target_domain_for_local_non_halo_element[nh_count6++] == my_rank)
22200 {
22201 // Add the element to the set of elements in the processor
22202 element_in_processor_pt.insert(ele_pt);
22203 }
22204
22205 } // if (!(ele_pt->is_halo()))
22206
22207 } // for (e < nelement_before_load_balance)
22208
22209 // Now include the received elements from the other processors
22210 // Loop over the processors
22211 for (unsigned iproc = 0; iproc < nproc; iproc++)
22212 {
22213 // No elements received from myself
22214 if (iproc != my_rank)
22215 {
22216 // Get the number of received elements with the "iproc"
22217 // processor
22218 const unsigned n_received_ele = received_elements_pt[iproc].size();
22219 for (unsigned ie = 0; ie < n_received_ele; ie++)
22220 {
22221 // Get the ie-th received element from processor iproc
22222 FiniteElement* ele_pt = received_elements_pt[iproc][ie];
22223
22224 // Include it in the set of elements in the processor
22225 element_in_processor_pt.insert(ele_pt);
22226
22227 } // for (ie < nreceived_ele)
22228
22229 } // if (iproc != my_rank)
22230
22231 } // for (iproc < nproc)
22232
22233 // Now create the shared boundaries
22234 create_new_shared_boundaries(element_in_processor_pt,
22235 new_shared_boundary_element_pt,
22236 new_shared_boundary_element_face_index);
22237
22238 // The time to create the new shared boundaries
22239 if (Print_timings_level_load_balance > 1)
22240 {
22242 << "CPU for creating new shared boundaries (load balance) [9]: "
22243 << TimingHelpers::timer() - tt_start_create_new_shared_boundaries
22244 << std::endl;
22245 }
22246
22247 // =====================================================================
22248 // END: CREATE THE NEW SHARED BOUNDARIES. BEFORE THE GENERATION OF
22249 // THE SHARED BOUNDARIES PUT IN A CONTAINER THOSE NONHALO ELEMENTS
22250 // THAT WILL REMAIN IN THE CURRENT PROCESSOR (BECAUSE THEIR RANK IS
22251 // THE SAME AS THE CURRENT PROCESSOR), AND THOSE ELEMENTS RECEIVED
22252 // FROM OTHER PROCESSORS. THESE SET OF ELEMENTS WILL BE USED TO
22253 // CHECK FOR POSSIBLE CONNECTIONS OF THE NEW SHARED BOUNDARIES WITH
22254 // THE ORIGINAL BOUNDARIES
22255 // =====================================================================
22256
22257 // =====================================================================
22258 // BEGIN: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22259 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22260 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22261 // =====================================================================
22262
22263 // Get the time to delete elements no longer belonging to the
22264 // processor
22265 double tt_start_delete_elements = 0.0;
22266 if (Print_timings_level_load_balance > 1)
22267 {
22268 tt_start_delete_elements = TimingHelpers::timer();
22269 }
22270
22271 // Once computed the new shared boundaries delete the elements that
22272 // no longer belong to the processor (including the old halo
22273 // elements)
22274
22275 // The procedure is similar to the one performed at the distribution
22276 // stage (src/generic/mesh.cc -- distribute() method)
22277
22278 // Clean the storage for halo(ed) elements/nodes
22279 this->Halo_node_pt.clear();
22280 this->Root_halo_element_pt.clear();
22281
22282 this->Haloed_node_pt.clear();
22283 this->Root_haloed_element_pt.clear();
22284
22285 // Mark all the nodes as obsolete
22286 const unsigned nnodes = this->nnode();
22287 for (unsigned j = 0; j < nnodes; j++)
22288 {
22289 this->node_pt(j)->set_obsolete();
22290 }
22291
22292 // Flush the mesh storage
22293 this->flush_element_storage();
22294
22295 // Delete any storage of external elements and nodes
22296 this->delete_all_external_storage();
22297
22298 // Clear external storage
22299 this->External_halo_node_pt.clear();
22300 this->External_halo_element_pt.clear();
22301
22302 this->External_haloed_node_pt.clear();
22303 this->External_haloed_element_pt.clear();
22304
22305 // Keep track of the deleted elements
22306 Vector<FiniteElement*> deleted_elements;
22307
22308 // Delete the elements that no longer belong to the processor
22309 unsigned nh_count7 = 0;
22310 for (unsigned e = 0; e < nelement_before_load_balance; e++)
22311 {
22312 FiniteElement* ele_pt = backed_up_ele_pt[e];
22313 // Only work with nonhalo elements
22314 if (!(ele_pt->is_halo()))
22315 {
22316 if (target_domain_for_local_non_halo_element[nh_count7++] == my_rank)
22317 {
22318 // Add the element to the mesh
22319 this->add_element_pt(ele_pt);
22320 // Get the number of nodes on the element
22321 const unsigned nele_nodes = ele_pt->nnode();
22322 // Loop over the nodes of the element
22323 for (unsigned j = 0; j < nele_nodes; j++)
22324 {
22325 // Mark the node as non-obsolete
22326 ele_pt->node_pt(j)->set_non_obsolete();
22327 } // for (j < nele_nodes)
22328
22329 } // The element belongs to the domain
22330 else
22331 {
22332 // Delete the element, but keep track of it
22333 deleted_elements.push_back(ele_pt);
22334 // Delete and point to null
22335 delete ele_pt;
22336 ele_pt = 0;
22337 }
22338
22339 } // if (!(ele_pt->is_halo()))
22340 else
22341 {
22342 // If the element is halo, delete if but keep track of it
22343 deleted_elements.push_back(ele_pt);
22344 // Delete and point to null
22345 delete ele_pt;
22346 ele_pt = 0;
22347 }
22348
22349 } // for (e < nelement_before_load_balance)
22350
22351 // Now add the received elements from each processor
22352 for (unsigned iproc = 0; iproc < nproc; iproc++)
22353 {
22354 if (iproc != my_rank)
22355 {
22356 // Get the number of received elements with the "iproc"
22357 // processor
22358 const unsigned nreceived_ele = received_elements_pt[iproc].size();
22359 for (unsigned ie = 0; ie < nreceived_ele; ie++)
22360 {
22361 // Get the element and add it to the mesh
22362 FiniteElement* ele_pt = received_elements_pt[iproc][ie];
22363 // Add the element to the mesh
22364 this->add_element_pt(ele_pt);
22365 // Get the number of nodes on the element
22366 const unsigned nele_nodes = ele_pt->nnode();
22367 // Loop over the nodes of the element
22368 for (unsigned j = 0; j < nele_nodes; j++)
22369 {
22370 // Mark the node as non-obsolete
22371 ele_pt->node_pt(j)->set_non_obsolete();
22372 } // for (j < nele_nodes)
22373
22374 } // for (ie < nreceived_ele)
22375
22376 } // if (iproc != my_rank)
22377
22378 } // for (iproc < nproc)
22379
22380 // Now remove the obsolete nodes
22381 this->prune_dead_nodes();
22382
22383 // The time to delete elements no longer belonging to the processor
22384 if (Print_timings_level_load_balance > 1)
22385 {
22386 oomph_info << "CPU for deleting elements no longer belonging to this "
22387 "processor (load balance) [10]: "
22388 << TimingHelpers::timer() - tt_start_delete_elements
22389 << std::endl;
22390 }
22391
22392 // =====================================================================
22393 // END: DELETE THE ELEMENTS NO LONGER BELONGING TO THE DOMAIN,
22394 // INCLUDING HALO ELEMENTS. ADD THE KEPT ELEMENTS TO THE MESH AND
22395 // THE RECEIVED ELEMENTS FROM OTHER PROCESSORS
22396 // =====================================================================
22397
22398 // =====================================================================
22399 // BEGIN: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS
22400 // (HALO NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING)
22401 // RESTORE THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS
22402 // ON EACH BOUNDARY
22403 // =====================================================================
22404
22405 // Get the time to re-establish the halo(ed) information
22406 double tt_start_re_etablish_halo_ed_info = 0.0;
22407 if (Print_timings_level_load_balance > 1)
22408 {
22409 tt_start_re_etablish_halo_ed_info = TimingHelpers::timer();
22410 }
22411
22412 // Prepare the data to re-establish the halo(ed) scheme
22413
22414 // Sort the nodes on the new shared boundaries so that they have the
22415 // same order on all processors
22416 this->sort_nodes_on_shared_boundaries();
22417
22418 // Before re-establish the halo and haloed elements save the number
22419 // of current elements in the boundaries, this will be useful to
22420 // re-establish the boundary elements. Notice that there may be
22421 // boundary elements with null pointers, since the element may no
22422 // longer belong to the current processor
22423 const unsigned tmp_nboundary = this->nboundary();
22424 Vector<unsigned> ntmp_boundary_elements(tmp_nboundary);
22425
22426 // If there are regions, save the number of boundary-region elements
22427 Vector<Vector<unsigned>> ntmp_boundary_elements_in_region(tmp_nboundary);
22428 // Are there regions?
22429 const unsigned n_regions = this->nregion();
22430
22431 // Loop over the boundaries
22432 for (unsigned ib = 0; ib < tmp_nboundary; ib++)
22433 {
22434 // Get the number of boundary elements
22435 ntmp_boundary_elements[ib] = this->nboundary_element(ib);
22436
22437 // Resize the container
22438 ntmp_boundary_elements_in_region[ib].resize(n_regions);
22439
22440 // Loop over the regions
22441 for (unsigned rr = 0; rr < n_regions; rr++)
22442 {
22443 // Get the region id
22444 const unsigned region_id =
22445 static_cast<unsigned>(this->region_attribute(rr));
22446
22447 // Store the number of element in the region (notice we are
22448 // using the region index not the region id to refer to the
22449 // region)
22450 ntmp_boundary_elements_in_region[ib][rr] =
22451 this->nboundary_element_in_region(ib, region_id);
22452
22453 } // for (rr < n_regions)
22454
22455 } // for (ib < tmp_nboundary)
22456
22457 // Re-establish the halo(ed) scheme
22458 this->reset_halo_haloed_scheme();
22459
22460 // Get the number of elements in the mesh after load balance
22461 const unsigned nelement_after_load_balance = this->nelement();
22462
22463 // We need to reset boundary elements because we need to get rid of
22464 // the old boundary elements and stay only with the new ones
22465 this->reset_boundary_element_info(ntmp_boundary_elements,
22466 ntmp_boundary_elements_in_region,
22467 deleted_elements);
22468
22469 // There is no need to re-set boundary coordinates since the
22470 // load-balanced mesh already has the correct information (the
22471 // boundary coordinate for each node was sent with the node
22472 // information)
22473
22474 // We need to re-compute the number of segments on each boundary
22475 // after load balance. It may be possible that the boundary is now
22476 // split in more segments, or that previous gaps between the
22477 // segments have now dissapeared because the received elements
22478 // filled those gaps
22479
22480 // In order to re-set the number of segments it is required to get
22481 // the face elements, attach them to create a contiguous
22482 // representation of the boundary (in segments possibly) and then
22483 // counter the number of segments. This can only be done after
22484 // restoring the boundary elements scheme (which has been done
22485 // above)
22486
22487 // Set the number of segments for the boundaries with geom objects
22488 // associated. The correct value is not on the original mesh since
22489 // it is computed only when calling then
22490 // setup_boundary_coordinates() method (called only for those
22491 // boundaries with no geom object associated)
22492 for (unsigned b = 0; b < tmp_nboundary; b++)
22493 {
22494 if (this->boundary_geom_object_pt(b) != 0)
22495 {
22496 // Clear the boundary segment nodes storage
22497 this->flush_boundary_segment_node(b);
22498
22499 // Dummy vector of nodes on segments
22500 Vector<Vector<Node*>> dummy_segment_node_pt;
22501
22502 // Compute the new number of segments in the boundary
22503 get_boundary_segment_nodes_helper(b, dummy_segment_node_pt);
22504
22505 // Get the number of segments from the vector of nodes
22506 const unsigned nsegments = dummy_segment_node_pt.size();
22507
22508 // Set the number of segments for the storing of the nodes
22509 // associated to the segments
22510 this->set_nboundary_segment_node(b, nsegments);
22511 } // if (this->boundary_geom_object_pt(b)!=0)
22512
22513 } // for (b < n_boundary)
22514
22515 // The time to re-establish the halo(ed) information
22516 if (Print_timings_level_load_balance > 1)
22517 {
22519 << "CPU for re-establishing halo(ed) information (load balance) [11]: "
22520 << TimingHelpers::timer() - tt_start_re_etablish_halo_ed_info
22521 << std::endl;
22522 }
22523
22524 // =====================================================================
22525 // END: REESTABLISH THE HALO(ED) SCHEME, ATTACH HALO ELEMENTS (HALO
22526 // NODES INCLUDED) TO THE NEW MESH (AFTER LOAD BALANCING) RESTORE
22527 // THE BOUNDARY ELEMENTS SCHEME AND THE NUMBER OF SEGMENTS ON EACH
22528 // BOUNDARY
22529 // =====================================================================
22530
22531 if (Print_timings_level_load_balance > 1)
22532 {
22533 oomph_info << "CPU for load balance [n_ele_before="
22534 << nelement_before_load_balance
22535 << ", n_ele_after=" << nelement_after_load_balance << "]: "
22536 << TimingHelpers::timer() - t_start_overall_load_balance
22537 << std::endl;
22538 }
22539
22540 oomph_info << "Load balance (unstructured mesh) [END]" << std::endl;
22541 }
22542
22543 //======================================================================
22544 /// Use the first and second group of elements to find the
22545 /// intersection between them to get the shared boundary
22546 /// elements from the first and second group
22547 //======================================================================
22548 template<class ELEMENT>
22551 const Vector<FiniteElement*>& first_element_pt,
22552 const Vector<FiniteElement*>& second_element_pt,
22553 Vector<FiniteElement*>& first_shared_boundary_element_pt,
22554 Vector<unsigned>& first_shared_boundary_element_face_index,
22555 Vector<FiniteElement*>& second_shared_boundary_element_pt,
22556 Vector<unsigned>& second_shared_boundary_element_face_index)
22557 {
22558 // 1) Compare their faces (nodes) and if they match then they are
22559 // part of a shared boundary
22560 // 2) Save the first and second group of elements that give rise to
22561 // the shared boundary, also include the face index
22562
22563 // Get the number of elements on the first group
22564 const unsigned nfirst_element = first_element_pt.size();
22565 // Loop over the elements in the first group
22566 for (unsigned ef = 0; ef < nfirst_element; ef++)
22567 {
22568 // Get the element
22569 FiniteElement* fele_pt = first_element_pt[ef];
22570 // Check if the element is halo
22571 bool first_ele_is_halo = false;
22572 if (fele_pt->is_halo())
22573 {
22574 first_ele_is_halo = true;
22575 }
22576 // Get each of the faces
22577 for (unsigned ifface = 0; ifface < 3; ifface++)
22578 {
22579 Vector<Node*> first_face(2);
22580 if (ifface == 0)
22581 {
22582 first_face[0] = fele_pt->node_pt(1);
22583 first_face[1] = fele_pt->node_pt(2);
22584 }
22585 else if (ifface == 1)
22586 {
22587 first_face[0] = fele_pt->node_pt(2);
22588 first_face[1] = fele_pt->node_pt(0);
22589 }
22590 else if (ifface == 2)
22591 {
22592 first_face[0] = fele_pt->node_pt(0);
22593 first_face[1] = fele_pt->node_pt(1);
22594 }
22595
22596 // Now check each of the faces with the faces on the second
22597 // elements
22598
22599 // Get the number of elements on the second group
22600 const unsigned nsecond_element = second_element_pt.size();
22601 // Loop over the elements in the second group
22602 for (unsigned es = 0; es < nsecond_element; es++)
22603 {
22604 // Get the element
22605 FiniteElement* sele_pt = second_element_pt[es];
22606 // Check if the element is halo
22607 bool second_ele_is_halo = false;
22608 if (sele_pt->is_halo())
22609 {
22610 second_ele_is_halo = true;
22611 }
22612 // Now check whether both elements are halo, if that is the
22613 // case then we go for the next elements. We can not look for
22614 // shared boundaries between halo elements since other
22615 // processors, those with the nonhalo counterpart of the
22616 // elements, are in charge of creating those shared boundaries
22617 if (!(first_ele_is_halo && second_ele_is_halo))
22618 {
22619 // Get each of the faces
22620 for (unsigned isface = 0; isface < 3; isface++)
22621 {
22622 Vector<Node*> second_face(2);
22623 if (isface == 0)
22624 {
22625 second_face[0] = sele_pt->node_pt(1);
22626 second_face[1] = sele_pt->node_pt(2);
22627 }
22628 else if (isface == 1)
22629 {
22630 second_face[0] = sele_pt->node_pt(2);
22631 second_face[1] = sele_pt->node_pt(0);
22632 }
22633 else if (isface == 2)
22634 {
22635 second_face[0] = sele_pt->node_pt(0);
22636 second_face[1] = sele_pt->node_pt(1);
22637 }
22638
22639 // Now check for any intersection among first and second
22640 // faces
22641 if (first_face[0] == second_face[0] &&
22642 first_face[1] == second_face[1])
22643 {
22644 // Save the elements on the corresponding containers
22645 first_shared_boundary_element_pt.push_back(fele_pt);
22646 // .. and the face index
22647 first_shared_boundary_element_face_index.push_back(ifface);
22648
22649 // Save the elements on the corresponding containers
22650 second_shared_boundary_element_pt.push_back(sele_pt);
22651 // .. and the face index
22652 second_shared_boundary_element_face_index.push_back(isface);
22653
22654 // Break the loop over the faces of the first elements
22655 // and the first elements, we need to continue looking
22656 // on the next face of the first elements
22657
22658 // Increase the indexes to force breaking the loop
22659 isface = 3;
22660 es = nsecond_element;
22661 }
22662 // Check for intersection with the reversed case too
22663 else if (first_face[0] == second_face[1] &&
22664 first_face[1] == second_face[0])
22665 {
22666 // Save the elements on the corresponding containers
22667 first_shared_boundary_element_pt.push_back(fele_pt);
22668 // .. and the face index
22669 first_shared_boundary_element_face_index.push_back(ifface);
22670
22671 // Save the elements on the corresponding containers
22672 second_shared_boundary_element_pt.push_back(sele_pt);
22673 // .. and the face index
22674 second_shared_boundary_element_face_index.push_back(isface);
22675
22676 // Break the loop over the faces of the first elements
22677 // and the first elements, we need to continue looking
22678 // on the next face of the first elements
22679
22680 // Increase the indexes to force breaking the loop
22681 isface = 3;
22682 es = nsecond_element;
22683 }
22684
22685 } // for (isface < 3)
22686
22687 } // if (!(first_ele_is_halo && second_ele_is_halo))
22688
22689 } // for (es < nsecond_element)
22690
22691 } // for (ifface < 3)
22692
22693 } // for (ef < nfirst_element)
22694 }
22695
22696 //======================================================================
22697 /// Creates the new shared boundaries, this method is also in
22698 /// charge of computing the shared boundaries ids of each processor
22699 /// and send that info. to all the processors
22700 //======================================================================
22701 template<class ELEMENT>
22703 std::set<FiniteElement*>& element_in_processor_pt,
22704 Vector<Vector<FiniteElement*>>& new_shared_boundary_element_pt,
22705 Vector<Vector<unsigned>>& new_shared_boundary_element_face_index)
22706 {
22707 // Get the number of processors
22708 const unsigned nproc = this->communicator_pt()->nproc();
22709 // Get the rank of the current processor
22710 const unsigned my_rank = this->communicator_pt()->my_rank();
22711
22712 // ================================================================
22713 // BEGIN: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22714 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22715 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22716 // SAME EDGE (INTERNAL BOUNDARIES)
22717 // ================================================================
22718
22719 // Get the time to get edges from shared boundary face elements
22720 double tt_start_get_edges_from_shd_bnd_face_ele = 0.0;
22721 if (Print_timings_level_load_balance > 2)
22722 {
22723 tt_start_get_edges_from_shd_bnd_face_ele = TimingHelpers::timer();
22724 }
22725
22726 // Face elements that create the shared boundaries (unsorted)
22727 Vector<Vector<FiniteElement*>> tmp_unsorted_face_ele_pt(nproc);
22728 // The elements from where the face element was created
22729 Vector<Vector<FiniteElement*>> tmp_unsorted_ele_pt(nproc);
22730 // The face index of the bulk element from where was created the
22731 // face element
22732 Vector<Vector<int>> tmp_unsorted_face_index_ele(nproc);
22733
22734 // Store the current edges lying on boundaries (this will help for
22735 // any edge of a shared boundary lying on an internal boundary)
22736 std::map<std::pair<Node*, Node*>, unsigned> elements_edges_on_boundary;
22737
22738 // Compute the edges on the other boundaries
22739 this->get_element_edges_on_boundary(elements_edges_on_boundary);
22740
22741 // Mark those edges (pair of nodes overlapped by a shared boundary)
22742 std::map<std::pair<Node*, Node*>, bool> overlapped_edge;
22743
22744 // Associate every found edge (face element) on the shared boundary
22745 // with an original boundary only if the edge (face element) lies
22746 // (overlaps) on an original boundary, it may happen only for
22747 // internal boundaries
22748 Vector<Vector<int>> tmp_edge_boundary(nproc);
22749
22750 // Get the face elements from the shared boundary elements with in
22751 // each processor
22752 for (unsigned iproc = 0; iproc < nproc; iproc++)
22753 {
22754 // There are no shared boundary elements with myself
22755 if (iproc != my_rank)
22756 {
22757 // Get the number of shared boundary elements with in "iproc"
22758 // processor
22759 const unsigned n_shared_bound_ele =
22760 new_shared_boundary_element_pt[iproc].size();
22761
22762 // Avoid to create repeated face elements, compare the nodes on
22763 // the edges of the face elements
22765
22766 // Count the number of repeated faces
22767 unsigned nrepeated_faces = 0;
22768
22769 // Loop over the shared boundary elements with the iproc
22770 // processor
22771 for (unsigned iele = 0; iele < n_shared_bound_ele; iele++)
22772 {
22773 // Get the bulk element
22774 FiniteElement* bulk_ele_pt =
22775 new_shared_boundary_element_pt[iproc][iele];
22776
22777 // Get the face index
22778 int face_index = static_cast<int>(
22779 new_shared_boundary_element_face_index[iproc][iele]);
22780
22781 // Create the face element
22782 FiniteElement* tmp_ele_pt =
22783 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
22784
22785 // Before adding the face element to the vector check that is
22786 // not has been previously created
22787 bool done_face = false;
22788
22789 // Get the number of nodes on the face element and get the first
22790 // and last node
22791 const unsigned nnode_face_ele = tmp_ele_pt->nnode();
22792 Node* first_face_node_pt = tmp_ele_pt->node_pt(0);
22793 Node* last_face_node_pt = tmp_ele_pt->node_pt(nnode_face_ele - 1);
22794
22795 // Get the number of already done face elements
22796 const unsigned ndone_faces = done_faces.size();
22797 // Loop over the already visited face elements
22798 for (unsigned n = 0; n < ndone_faces; n++)
22799 {
22800 Node* first_done_face_node_pt = done_faces[n].first;
22801 Node* second_done_face_node_pt = done_faces[n].second;
22802 if (first_face_node_pt == first_done_face_node_pt &&
22803 last_face_node_pt == second_done_face_node_pt)
22804 {
22805 done_face = true;
22806 nrepeated_faces++;
22807 break;
22808 }
22809 // Check for the reversed case
22810 else if (first_face_node_pt == second_done_face_node_pt &&
22811 last_face_node_pt == first_done_face_node_pt)
22812 {
22813 done_face = true;
22814 nrepeated_faces++;
22815 break;
22816 }
22817
22818 } // for (n < ndone_faces)
22819
22820 // Only include the faces that are not repeated
22821 if (!done_face)
22822 {
22823 // Add the face element in the vector
22824 tmp_unsorted_face_ele_pt[iproc].push_back(tmp_ele_pt);
22825 // Add the bulk element to the vector
22826 tmp_unsorted_ele_pt[iproc].push_back(bulk_ele_pt);
22827 // Add the face index to the vector
22828 tmp_unsorted_face_index_ele[iproc].push_back(face_index);
22829 // Include the nodes in the done nodes vector
22830 std::pair<Node*, Node*> tmp_edge =
22831 std::make_pair(first_face_node_pt, last_face_node_pt);
22832 // Push the edge
22833 done_faces.push_back(tmp_edge);
22834
22835 // Associate the face element with a boundary (if that is
22836 // the case)
22837 int edge_boundary_id = -1;
22838 std::map<std::pair<Node*, Node*>, unsigned>::iterator it;
22839 it = elements_edges_on_boundary.find(tmp_edge);
22840 // If the edges lie on a boundary then get the boundary id
22841 // on which the edges lie
22842 if (it != elements_edges_on_boundary.end())
22843 {
22844 // Assign the internal boundary id associated with the
22845 // edge
22846 edge_boundary_id = (*it).second;
22847 // Mark the edge as overlapped
22848 overlapped_edge[tmp_edge] = true;
22849 // Also include the reversed version of the edge
22850 std::pair<Node*, Node*> rev_tmp_edge =
22851 std::make_pair(last_face_node_pt, first_face_node_pt);
22852 // Mark the reversed version of the edge as overlapped
22853 overlapped_edge[rev_tmp_edge] = true;
22854 }
22855 else
22856 {
22857 // Look for the reversed version
22858 std::pair<Node*, Node*> rtmp_edge =
22859 std::make_pair(last_face_node_pt, first_face_node_pt);
22860 it = elements_edges_on_boundary.find(rtmp_edge);
22861 if (it != elements_edges_on_boundary.end())
22862 {
22863 // Assign the internal boundary id associated with the
22864 // edge
22865 edge_boundary_id = (*it).second;
22866 // Mark the edge as overlapped
22867 overlapped_edge[rtmp_edge] = true;
22868 // Mark the reversed version (normal) of the edge as
22869 // overlapped
22870 overlapped_edge[tmp_edge] = true;
22871 }
22872 }
22873 // Associate the edge with a boundary
22874 tmp_edge_boundary[iproc].push_back(edge_boundary_id);
22875 } // if (!done_face)
22876 else
22877 {
22878 // Delete the repeated face elements
22879 delete tmp_ele_pt;
22880 tmp_ele_pt = 0;
22881 }
22882
22883 } // for (iele < n_shared_bound_ele)
22884
22885 } // if (iproc != my_rank)
22886
22887 } // for (iproc < nproc)
22888
22889 // The time to get edges from shared boundary face elements
22890 if (Print_timings_level_load_balance > 2)
22891 {
22892 oomph_info << "CPU for getting edges from shared boundary face elements "
22893 "(load balance) [9.1]: "
22895 tt_start_get_edges_from_shd_bnd_face_ele
22896 << std::endl;
22897 }
22898
22899 // ================================================================
22900 // END: GET THE SHARED BOUNDARY FACE ELEMENTS FROM THE SHARED
22901 // BOUNDARY ELEMENTS, AND ASSIGN A ROOT EDGE TO EACH FACE
22902 // ELEMENT. AVOID THE CREATION OF FACE ELEMENTS THAT REPRESENT THE
22903 // SAME EDGE (INTERNAL BOUNDARIES)
22904 // ================================================================
22905
22906 // ================================================================
22907 // BEGIN: BEFORE SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED
22908 // DATA, WE NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE
22909 // SAME ORDER IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE
22910 // THE BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE
22911 // SAME ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
22912 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
22913 // ================================================================
22914
22915 // Get the time to sort shared face elements
22916 double tt_start_sort_shared_face_elements = 0.0;
22917 if (Print_timings_level_load_balance > 2)
22918 {
22919 tt_start_sort_shared_face_elements = TimingHelpers::timer();
22920 }
22921
22922 // -----------------------------------------------------------------
22923 // Before continuing we need to ensured that the face elements are
22924 // stored in the same order in all processors. Sort them starting
22925 // from the face element with the bottom-left node coordinate
22926
22927 // Face elements that create the shared boundaries (unsorted)
22928 Vector<Vector<FiniteElement*>> unsorted_face_ele_pt(nproc);
22929 // The elements from where the face element was created
22930 Vector<Vector<FiniteElement*>> unsorted_ele_pt(nproc);
22931 // The face index of the bulk element from where was created the
22932 // face element
22933 Vector<Vector<int>> unsorted_face_index_ele(nproc);
22934 // Associate every found edge on the shared boundary with an
22935 // original boundary only if the edge lies on an original boundary,
22936 // it may happen only for internal boundaries
22937 Vector<Vector<int>> edge_boundary(nproc);
22938
22939 // For each face element, mark if the element should be considered
22940 // in its inverted way to fullfill with the bottom-left node to be
22941 // the first (left) node. First get the status of each element and
22942 // when they get sorted copy the values across
22943 std::vector<std::vector<bool>> tmp_treat_as_inverted(nproc);
22944 // Vector to store the status of the sorted face elements based on
22945 // the bottom-left condition
22946 std::vector<std::vector<bool>> treat_as_inverted(nproc);
22947
22948 // Get the bottom-left node of each face element and sort them
22949 // starting from the face element with the bottom-left node
22950
22951 // Loop over the processors
22952 for (unsigned iproc = 0; iproc < nproc; iproc++)
22953 {
22954 // There are no shared face elements with myself
22955 if (iproc != my_rank)
22956 {
22957 // Get the number of unsorted face elements
22958 const unsigned n_face_ele = tmp_unsorted_face_ele_pt[iproc].size();
22959 // Store the centroid of the face element. Perform the sorting
22960 // based on the bottom-left centroid of each face element
22961 Vector<Vector<double>> centroid_vertices(n_face_ele);
22962
22963 // Resize the storage for the treating as inverted face element
22964 // storage
22965 tmp_treat_as_inverted[iproc].resize(n_face_ele);
22966
22967 // Loop over the face elements associated with the iproc
22968 // processor
22969 for (unsigned e = 0; e < n_face_ele; e++)
22970 {
22971 // Get the face element
22972 FiniteElement* face_ele_pt = tmp_unsorted_face_ele_pt[iproc][e];
22973 // Get the number of nodes of the face element
22974 const unsigned n_node = face_ele_pt->nnode();
22975 Vector<double> bottom_left(2);
22976 // Assign as the bottom-left node the first node
22977 // Get the node
22978 Node* node_pt = face_ele_pt->node_pt(0);
22979 bottom_left[0] = node_pt->x(0);
22980 bottom_left[1] = node_pt->x(1);
22981 // Set as not treat as inverted element
22982 tmp_treat_as_inverted[iproc][e] = false;
22983 // Loop over the nodes to get the bottom-left vertex of all
22984 // the nodes
22985 for (unsigned n = 1; n < n_node; n++)
22986 {
22987 // Get the node
22988 Node* node_pt = face_ele_pt->node_pt(n);
22989 if (node_pt->x(1) < bottom_left[1])
22990 {
22991 bottom_left[0] = node_pt->x(0);
22992 bottom_left[1] = node_pt->x(1);
22993 // The first node is no longer the bottom-left node, we
22994 // need to treat the element as inverted
22995 tmp_treat_as_inverted[iproc][e] = true;
22996 } // if (node_pt->x(1) < bottom_left[1])
22997 else if (node_pt->x(1) == bottom_left[1])
22998 {
22999 if (node_pt->x(0) < bottom_left[0])
23000 {
23001 bottom_left[0] = node_pt->x(0);
23002 bottom_left[1] = node_pt->x(1);
23003 // The first node is no longer the bottom-left node, we
23004 // need to treat the element as inverted
23005 tmp_treat_as_inverted[iproc][e] = true;
23006 } // if (node_pt->x(0) < bottom_left[0])
23007 } // else if (node_pt->x(1) == bottom_left[1])
23008
23009 } // for (n < n_node
23010
23011 // Resize the container
23012 centroid_vertices[e].resize(2);
23013 // Add the centroid of the face element
23014 centroid_vertices[e][0] = (face_ele_pt->node_pt(0)->x(0) +
23015 face_ele_pt->node_pt(n_node - 1)->x(0)) *
23016 0.5;
23017 centroid_vertices[e][1] = (face_ele_pt->node_pt(0)->x(1) +
23018 face_ele_pt->node_pt(n_node - 1)->x(1)) *
23019 0.5;
23020
23021 } // for (e < n_face_ele)
23022
23023 // Sort the face elements based on their bottom-left node
23024 unsigned n_sorted_bottom_left = 0;
23025 // Keep track of the already sorted face elements
23026 std::vector<bool> done_face(n_face_ele, false);
23027
23028 // Loop until all face elements have been sorted
23029 while (n_sorted_bottom_left < n_face_ele)
23030 {
23031 // The index of the next bottom-left face element
23032 unsigned index = 0;
23033 Vector<double> current_bottom_left(2);
23034 for (unsigned e = 0; e < n_face_ele; e++)
23035 {
23036 // Get the first not done face element
23037 if (!done_face[e])
23038 {
23039 // Store the first not done
23040 current_bottom_left[0] = centroid_vertices[e][0];
23041 current_bottom_left[1] = centroid_vertices[e][1];
23042 // Set the index
23043 index = e;
23044 // Break
23045 break;
23046 } // if (!done_face[e])
23047
23048 } // for (e < n_face_ele)
23049
23050 // Loop over all the other nondone face elements
23051 for (unsigned e = index + 1; e < n_face_ele; e++)
23052 {
23053 // Get the first not done face element
23054 if (!done_face[e])
23055 {
23056 if (centroid_vertices[e][1] < current_bottom_left[1])
23057 {
23058 // Re-set the current bottom left vertex
23059 current_bottom_left[0] = centroid_vertices[e][0];
23060 current_bottom_left[1] = centroid_vertices[e][1];
23061 // Re-assign the index
23062 index = e;
23063 } // if (centroid_vertices[e][1] < current_bottom_left[1])
23064 else if (centroid_vertices[e][1] == current_bottom_left[1])
23065 {
23066 if (centroid_vertices[e][0] < current_bottom_left[0])
23067 {
23068 // Re-set the current bottom left vertex
23069 current_bottom_left[0] = centroid_vertices[e][0];
23070 current_bottom_left[1] = centroid_vertices[e][1];
23071 // Re-assign the index
23072 index = e;
23073 } // if (centroid_vertices[e][0] < current_bottom_left[0])
23074
23075 } // else if (centroid_vertices[e][1] == current_bottom_left[1])
23076
23077 } // if (!done_face[e])
23078
23079 } // for (e < n_face_ele)
23080
23081 // The face element
23082 unsorted_face_ele_pt[iproc].push_back(
23083 tmp_unsorted_face_ele_pt[iproc][index]);
23084 // The boundary element
23085 unsorted_ele_pt[iproc].push_back(tmp_unsorted_ele_pt[iproc][index]);
23086 // The face index
23087 unsorted_face_index_ele[iproc].push_back(
23088 tmp_unsorted_face_index_ele[iproc][index]);
23089 // The edge boundary associated to the face element
23090 edge_boundary[iproc].push_back(tmp_edge_boundary[iproc][index]);
23091 // The treat as inverted condition
23092 treat_as_inverted[iproc].push_back(
23093 tmp_treat_as_inverted[iproc][index]);
23094
23095 // Mark the face element as sorted (done or visited)
23096 done_face[index] = true;
23097
23098 // Increase the number of sorted bottom-left face elements
23099 n_sorted_bottom_left++;
23100
23101 } // while (n_sorted_bottom_left < n_face_ele)
23102
23103#ifdef PARANOID
23104 // Get the number of face elements sorted with the bottom-left
23105 // condition
23106 const unsigned tmp_n_face_ele = unsorted_face_ele_pt[iproc].size();
23107
23108 if (tmp_n_face_ele != n_face_ele)
23109 {
23110 std::ostringstream error_stream;
23111 error_stream
23112 << "The number of face elements before sorting them starting\n"
23113 << "from their bottom-left vertex is different from the number\n"
23114 << "of face elements after the sorting\n"
23115 << "N. ele before sorting: (" << n_face_ele << ")\n"
23116 << "N. ele after sorting: (" << tmp_n_face_ele << ")\n";
23117 throw OomphLibError(
23118 error_stream.str(),
23119 "RefineableTriangleMesh::create_new_shared_boundaries()",
23120 OOMPH_EXCEPTION_LOCATION);
23121 }
23122#endif
23123
23124 } // if (iproc != my_rank)
23125
23126 } // for (iproc < nproc)
23127
23128 // The time to sort shared face elements
23129 if (Print_timings_level_load_balance > 2)
23130 {
23131 oomph_info << "CPU for sorting shared boundary face elements (load "
23132 "balance) [9.2]: "
23133 << TimingHelpers::timer() - tt_start_sort_shared_face_elements
23134 << std::endl;
23135 }
23136
23137 // ================================================================
23138 // END: SORTING THE SHARED FACE ELEMENTS AND ITS ASSOCIATED DATA, WE
23139 // NEED TO ENSURE THAT THEY APPEAR (OR ARE STORED) IN THE SAME ORDER
23140 // IN BOTH OF THE PROCESSORS THAT CREATED THEM. WE USE THE
23141 // BOTTOM-LEFT NODE OF EACH FACE ELEMENT TO STORE THEM IN THE SAME
23142 // ORDER IN BOTH PROCESSORS. ALSO ENSURE THAT THE FACE ELEMENTS
23143 // AGREE WITH THE FIRST AND LAST NODE IN ALL PROCESSORS
23144 // ================================================================
23145
23146 // ================================================================
23147 // BEGIN: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23148 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23149 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23150 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23151 // IS THE CASE)
23152 // ================================================================
23153
23154 // Get the time to compute the valency of each node
23155 double tt_start_compute_valency_of_nodes = 0.0;
23156 if (Print_timings_level_load_balance > 2)
23157 {
23158 tt_start_compute_valency_of_nodes = TimingHelpers::timer();
23159 }
23160
23161 // Stores the global-degree of each node
23162 std::map<Node*, unsigned> global_node_degree;
23163
23164 // Get the global degree (valency) of each node
23165 compute_shared_node_degree_helper(unsorted_face_ele_pt, global_node_degree);
23166
23167 // The time to compute the valency of each node
23168 if (Print_timings_level_load_balance > 2)
23169 {
23171 << "CPU for computing the valency of nodes (load balance) [9.3]: "
23172 << TimingHelpers::timer() - tt_start_compute_valency_of_nodes
23173 << std::endl;
23174 }
23175
23176 // ================================================================
23177 // END: COMPUTE THE GLOBAL DEGREE (VALENCY OF EACH NODE). THE
23178 // DEGREE OF THE NODES IN THE CURRENT SHARED BOUNDARIES IS COMPUTED
23179 // FIRST, THEN THIS INFO. IS SENT TO A ROOT PROCESSOR WHICH IS IN
23180 // CHARGE OF IDENTIFY AND RE-ASSIGN THE DEGREE OF THE NODES (IF THAT
23181 // IS THE CASE)
23182 // ================================================================
23183
23184 // ================================================================
23185 // BEGIN: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23186 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23187 // ================================================================
23188
23189 // Get the time to compute nodes on non overlapped shared boundaries
23190 double tt_start_nodes_on_non_overlapped_shd_bnd = 0.0;
23191 if (Print_timings_level_load_balance > 2)
23192 {
23193 tt_start_nodes_on_non_overlapped_shd_bnd = TimingHelpers::timer();
23194 }
23195
23196 // Mark the nodes on original boundaries not overlapped by shared
23197 // boundaries
23198 std::map<unsigned, std::map<Node*, bool>>
23199 node_on_bnd_not_overlapped_by_shd_bnd;
23200
23201 // Loop over the edges of the original boundaries
23202 for (std::map<std::pair<Node*, Node*>, unsigned>::iterator it_map =
23203 elements_edges_on_boundary.begin();
23204 it_map != elements_edges_on_boundary.end();
23205 it_map++)
23206 {
23207 // Get the edge
23208 std::pair<Node*, Node*> edge_pair = (*it_map).first;
23209 // Is the edge overlaped by a shared boundary
23210 if (!overlapped_edge[edge_pair])
23211 {
23212 // Mark the nodes of the edge as being on an edge not overlaped
23213 // by a shared boundary on the boundary the edge is
23214 unsigned b = (*it_map).second;
23215
23216 // Get the left node
23217 Node* left_node_pt = edge_pair.first;
23218 node_on_bnd_not_overlapped_by_shd_bnd[b][left_node_pt] = true;
23219
23220 // Get the right node
23221 Node* right_node_pt = edge_pair.second;
23222 node_on_bnd_not_overlapped_by_shd_bnd[b][right_node_pt] = true;
23223
23224 } // if (!overlapped_edge[edge_pair])
23225
23226 } // Loop over edges to mark those nodes on overlaped edge by
23227 // shared boundaries
23228
23229 // The time to compute nodes on non overlapped shared boundaries
23230 if (Print_timings_level_load_balance > 2)
23231 {
23232 oomph_info << "CPU for computing nodes on non overlapped shared "
23233 "boundaries (load balance) [9.4]: "
23235 tt_start_nodes_on_non_overlapped_shd_bnd
23236 << std::endl;
23237 }
23238
23239 // ================================================================
23240 // END: IDENTIFY THE NODES LYING ON EDGES NOT OVERLAPED BY SHARED
23241 // BOUNDARIES, IDENTIFY THE BOUNDARY TO WHICH THE EDGE CORRESPOND
23242 // ================================================================
23243
23244 // ==================================================================
23245 // BEGIN: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS
23246 // TO THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN
23247 // THE MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF
23248 // ANOTHER BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS
23249 // BEING CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE
23250 // CASE WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF
23251 // A BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23252 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23253 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23254 // ==================================================================
23255
23256 // Get the time to sort shared boundaries face elements to create a
23257 // continuous representation of the boundary
23258 double tt_start_join_shd_bnd_face_ele = 0.0;
23259 if (Print_timings_level_load_balance > 2)
23260 {
23261 tt_start_join_shd_bnd_face_ele = TimingHelpers::timer();
23262 }
23263
23264 // Face elements that create the shared boundaries (sorted)
23265 Vector<Vector<Vector<FiniteElement*>>> sorted_face_ele_pt(nproc);
23266
23267 // Bulk elements that create the shared boundaries (sorted)
23268 Vector<Vector<Vector<FiniteElement*>>> sorted_ele_pt(nproc);
23269
23270 // Face indexes of the bulk elements that create the shared
23271 // boundaries (sorted)
23272 Vector<Vector<Vector<int>>> sorted_face_index_ele(nproc);
23273
23274 // Store the edge boundary id associated with a shared boundary (if
23275 // any, this apply for shared boundaries lying on internal
23276 // boundaries, then the shared boundary is marked as overlaping an
23277 // internal boundary)
23278 Vector<Vector<int>> edge_boundary_id(nproc);
23279
23280 // Store the connection information obtained when joining the face
23281 // elements (used for connection purposes only)
23282 Vector<Vector<Vector<int>>> sorted_connection_info(nproc);
23283
23284 // Store the local shared boundary id associated to the elements
23285 // that will give rise to the shared boundaries (used to compute the
23286 // global shared boundary id from the local shared boundary id)
23287 Vector<Vector<unsigned>> proc_local_shared_boundary_id(nproc);
23288
23289 // Map that associates the local shared boundary id with the list of
23290 // nodes that create it
23291 std::map<unsigned, std::list<Node*>>
23292 local_shd_bnd_id_to_sorted_list_node_pt;
23293
23294 // Local shared bouonday id (used to locally identify the lists of
23295 // nodes that create shared boundaries, it is also useful to
23296 // identify connections with shared boundaries)
23297 unsigned local_shd_bnd_id = this->Initial_shared_boundary_id;
23298
23299 // Sort the face elements, using the nodes at its ends
23300
23301 // Mark the done elements
23302 std::map<FiniteElement*, bool> done_ele;
23303
23304 // Mark the inverted elements
23305 std::map<FiniteElement*, bool> is_inverted;
23306
23307 // Sort the face elements to get the number of shared boundaries
23308 // with in each processor
23309 for (unsigned iproc = 0; iproc < nproc; iproc++)
23310 {
23311 // No face elements with myself
23312 if (iproc != my_rank)
23313 {
23314 // Get the number of unsorted face elements with the iproc
23315 // processor
23316 const unsigned nunsorted_face_ele = unsorted_face_ele_pt[iproc].size();
23317 // Count the number of sorted face elements
23318 unsigned nsorted_face_ele = 0;
23319
23320 // Iterate until all the face elements have been sorted
23321 while (nsorted_face_ele < nunsorted_face_ele)
23322 {
23323 // Take the first nonsorted element an use it as root element,
23324 // add elements to the left and right until no more elements
23325 // left or until a stop condition is reached (connection,
23326 // boundary node)
23327
23328#ifdef PARANOID
23329 // Flag to indicate if a root element was found
23330 bool found_root_element = false;
23331#endif
23332
23333 // Index of the found root element
23334 unsigned root_index = 0;
23335
23336 // List that contains the sorted face elements
23337 std::list<FiniteElement*> tmp_sorted_face_ele_pt;
23338
23339 // List that contains the sorted elements
23340 std::list<FiniteElement*> tmp_sorted_ele_pt;
23341
23342 // List that contains the sorted face indexes of the bulk
23343 // elements
23344 std::list<int> tmp_sorted_face_index_ele;
23345
23346 // Storing for the sorting nodes extracted from the face
23347 // elements. The sorted nodes are used to identify connections
23348 // among new shared boundaries or original boundaries
23349 std::list<Node*> tmp_sorted_nodes_pt;
23350 // Clear the storage (just in case)
23351 tmp_sorted_nodes_pt.clear();
23352
23353 // The initial and final nodes
23354 Node* initial_node_pt = 0;
23355 Node* final_node_pt = 0;
23356
23357 // Store the original boundary id related with the root face
23358 // element (if there is one)
23359 int root_edge_bound_id = -1;
23360
23361 // Loop over the unsorted face elements until a root element
23362 // is found
23363 for (unsigned e = 0; e < nunsorted_face_ele; e++)
23364 {
23365 // Get a root element
23366 FiniteElement* root_ele_pt = unsorted_face_ele_pt[iproc][e];
23367 // Is the element already done?
23368 if (!done_ele[root_ele_pt])
23369 {
23370 // Get the edge boundary id associated with the edge (if
23371 // there is one)
23372 root_edge_bound_id = edge_boundary[iproc][e];
23373 // Add the face element to the list of sorted face
23374 // elements
23375 tmp_sorted_face_ele_pt.push_back(root_ele_pt);
23376 // Add the bulk element to the list of sorted elements
23377 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23378 // Add the face index to the list of sorted face index
23379 // elements
23380 tmp_sorted_face_index_ele.push_back(
23381 unsorted_face_index_ele[iproc][e]);
23382
23383 // Get the nodes and state them as initial and final
23384 const unsigned nnodes = root_ele_pt->nnode();
23385 // Check if the face element should be treated as inverted
23386 if (!treat_as_inverted[iproc][e])
23387 {
23388 initial_node_pt = root_ele_pt->node_pt(0);
23389 final_node_pt = root_ele_pt->node_pt(nnodes - 1);
23390 }
23391 else
23392 {
23393 initial_node_pt = root_ele_pt->node_pt(nnodes - 1);
23394 final_node_pt = root_ele_pt->node_pt(0);
23395 }
23396 // Add both nodes to the list of sorted nodes
23397 tmp_sorted_nodes_pt.push_back(initial_node_pt);
23398 tmp_sorted_nodes_pt.push_back(final_node_pt);
23399
23400 // Mark the element as done
23401 done_ele[root_ele_pt] = true;
23402 // Check if the face element should be treated as inverted
23403 if (!treat_as_inverted[iproc][e])
23404 {
23405 // Mark the element as not inverted
23406 is_inverted[root_ele_pt] = false;
23407 }
23408 else
23409 {
23410 // Mark the element as inverted
23411 is_inverted[root_ele_pt] = true;
23412 }
23413 // Increase the counter for sorted face elements
23414 nsorted_face_ele++;
23415 // Set the root index
23416 root_index = e;
23417#ifdef PARANOID
23418 // Set the flag of found root element
23419 found_root_element = true;
23420#endif
23421 // Break the loop
23422 break;
23423
23424 } // if (!done_ele[root_ele_pt])
23425
23426 } // for (e < nunsorted_face_ele)
23427
23428#ifdef PARANOID
23429 if (!found_root_element)
23430 {
23431 std::ostringstream error_stream;
23432 error_stream
23433 << "It was not possible the found the root element\n\n";
23434 throw OomphLibError(error_stream.str(),
23435 OOMPH_CURRENT_FUNCTION,
23436 OOMPH_EXCEPTION_LOCATION);
23437 }
23438#endif
23439
23440 // New element added. Continue adding elements -- or nodes --
23441 // to the list of shared boundary elements while a new element
23442 // has been added to the list (we have just added the root
23443 // element)
23444 bool new_element_added = true;
23445
23446 // Similarly that in the
23447 // "create_polylines_from_halo_elements_helper() method, we
23448 // extract the nodes (in order) that will create the shared
23449 // polyline, and also check for connections with the just
23450 // added face elements (nodes)
23451
23452 // Flags to indicate at which end (of the sorted list of
23453 // boundary elements) the element was added (left or right)
23454 bool element_added_to_the_left = false;
23455 bool element_added_to_the_right = false;
23456
23457 // Flag to indicate that the "left" node of the element added
23458 // to the left was found to be shared with another boundary
23459 bool connection_to_the_left = false;
23460
23461 // Flag to indicate that the "right" node of the element added
23462 // to the right was found to be shared with another boundary
23463 bool connection_to_the_right = false;
23464
23465 // Flag to stop the adding of elements (and nodes) to the
23466 // current shared boundary (because there are connections at
23467 // both ends)
23468 bool current_polyline_has_connections_at_both_ends = false;
23469
23470 // Store the boundary ids of the polylines to connect (only
23471 // used when the polyline was found to have a connection)
23472 // -1: Indicates no connection
23473 // -2: Indicates connection with itself
23474 // -3: Indicates no connection BUT STOP adding elements
23475 // -because the node is a boundary node whose boundary is no
23476 // -currently part of the domain. Think in one of the corner
23477 // -nodes of a triangle touchin a boundary that does no longer
23478 // -exist
23479 // Any other value: Boundary id to connect
23480 int bound_id_connection_to_the_left = -1;
23481 int bound_id_connection_to_the_right = -1;
23482
23483 // Get the global degree of the node (notice the local degree
23484 // has been updated to global degree)
23485 const unsigned initial_node_degree =
23486 global_node_degree[initial_node_pt];
23487
23488 // Flag to indicate we are calling the method from a load
23489 // balance sub-rutine
23490 const bool called_for_load_balance = true;
23491
23492 // Check if the nodes of the root element have connections
23493 // ... to the left
23494 bound_id_connection_to_the_left =
23495 this->check_connections_of_polyline_nodes(
23496 element_in_processor_pt,
23497 root_edge_bound_id,
23498 overlapped_edge,
23499 node_on_bnd_not_overlapped_by_shd_bnd,
23500 tmp_sorted_nodes_pt,
23501 local_shd_bnd_id_to_sorted_list_node_pt,
23502 initial_node_degree,
23503 initial_node_pt,
23504 called_for_load_balance);
23505
23506 // If there is a stop condition then set the corresponding
23507 // flag
23508 if (bound_id_connection_to_the_left != -1)
23509 {
23510 connection_to_the_left = true;
23511 } // if (bound_id_connection_to_the_left != -1)
23512
23513 // Get the global degree of the node (notice the local degree
23514 // has been updated to global degree)
23515 const unsigned final_node_degree = global_node_degree[final_node_pt];
23516
23517 // ... and to the right
23518 bound_id_connection_to_the_right =
23519 this->check_connections_of_polyline_nodes(
23520 element_in_processor_pt,
23521 root_edge_bound_id,
23522 overlapped_edge,
23523 node_on_bnd_not_overlapped_by_shd_bnd,
23524 tmp_sorted_nodes_pt,
23525 local_shd_bnd_id_to_sorted_list_node_pt,
23526 final_node_degree,
23527 final_node_pt,
23528 called_for_load_balance);
23529
23530 // If there is a stop condition then set the corresponding
23531 // flag
23532 if (bound_id_connection_to_the_right != -1)
23533 {
23534 connection_to_the_right = true;
23535 } // if (bound_id_connection_to_the_right != -1)
23536
23537 // If the current shared boundary has connections at both ends
23538 // then stop the adding of elements (and nodes)
23539 if (connection_to_the_left && connection_to_the_right)
23540 {
23541 current_polyline_has_connections_at_both_ends = true;
23542 }
23543
23544 // Continue searching for more elements to add if
23545 // 1) A new element was added at the left or right of the list
23546 // 2) There are more possible elements to add
23547 // 3) The nodes at the edges of the added element (left or
23548 // right) are not part of any other previous shared
23549 // boundary
23550 while (new_element_added && (nsorted_face_ele < nunsorted_face_ele) &&
23551 !current_polyline_has_connections_at_both_ends)
23552 {
23553 // Loop over the remaining elements and try to create a
23554 // contiguous set of face elements, start looking from the
23555 // root index. Any previous element should have been already
23556 // visited
23557 for (unsigned e = root_index; e < nunsorted_face_ele; e++)
23558 {
23559 // Reset the flags for added elements, to the left and right
23560 new_element_added = false;
23561 element_added_to_the_left = false;
23562 element_added_to_the_right = false;
23563
23564 // Get the "e"-th element on the vector
23565 FiniteElement* tmp_ele_pt = unsorted_face_ele_pt[iproc][e];
23566 // Get the boundary id associated with the edge (if any)
23567 const int edge_bound_id = edge_boundary[iproc][e];
23568 // Check if the element has been already sorted and the
23569 // related edge bound id is the same as the root edge (if
23570 // any)
23571 if (!done_ele[tmp_ele_pt] &&
23572 (edge_bound_id == root_edge_bound_id))
23573 {
23574 // Get the number of nodes on the current element
23575 const unsigned nnodes = tmp_ele_pt->nnode();
23576 // Get the first and last node of the element
23577 // Check if the face element should be treated as inverted
23578 Node* first_node_pt = 0;
23579 Node* last_node_pt = 0;
23580 if (!treat_as_inverted[iproc][e])
23581 {
23582 first_node_pt = tmp_ele_pt->node_pt(0);
23583 last_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23584 }
23585 else
23586 {
23587 first_node_pt = tmp_ele_pt->node_pt(nnodes - 1);
23588 last_node_pt = tmp_ele_pt->node_pt(0);
23589 }
23590
23591 // A pointer to the node at the left or right of the
23592 // just added element, the most left or the most right
23593 // node
23594 Node* new_added_node_pt = 0;
23595
23596 // Check if the element goes to the left
23597 if (initial_node_pt == last_node_pt && !connection_to_the_left)
23598 {
23599 // Update the initial node and the just added node
23600 new_added_node_pt = initial_node_pt = first_node_pt;
23601 // Add the most left node
23602 tmp_sorted_nodes_pt.push_front(first_node_pt);
23603 // Add the face element to the list of sorted face
23604 // elements
23605 tmp_sorted_face_ele_pt.push_front(tmp_ele_pt);
23606 // Add the bulk element to the list of sorted elements
23607 tmp_sorted_ele_pt.push_front(unsorted_ele_pt[iproc][e]);
23608 // Add the face index to the list of sorted face index
23609 // elements
23610 tmp_sorted_face_index_ele.push_front(
23611 unsorted_face_index_ele[iproc][e]);
23612 if (!treat_as_inverted[iproc][e])
23613 {
23614 // Mark the element as not inverted
23615 is_inverted[tmp_ele_pt] = false;
23616 }
23617 else
23618 {
23619 // Mark the element as inverted
23620 is_inverted[tmp_ele_pt] = true;
23621 }
23622 // Set the flag to indicate a new element was added
23623 new_element_added = true;
23624 // Set the flag to indicate the element was added to
23625 // the left
23626 element_added_to_the_left = true;
23627 }
23628 // Check if the element goes to the left (but inverted)
23629 else if (initial_node_pt == first_node_pt &&
23630 !connection_to_the_left)
23631 {
23632 // Update the initial node and the just added node
23633 new_added_node_pt = initial_node_pt = last_node_pt;
23634 // Add the most left node
23635 tmp_sorted_nodes_pt.push_front(last_node_pt);
23636 // Add the face element to the list of sorted face
23637 // elements
23638 tmp_sorted_face_ele_pt.push_front(tmp_ele_pt);
23639 // Add the bulk element to the list of sorted elements
23640 tmp_sorted_ele_pt.push_front(unsorted_ele_pt[iproc][e]);
23641 // Add the face index to the list of sorted face index
23642 // elements
23643 tmp_sorted_face_index_ele.push_front(
23644 unsorted_face_index_ele[iproc][e]);
23645 if (!treat_as_inverted[iproc][e])
23646 {
23647 // Mark the element as inverted
23648 is_inverted[tmp_ele_pt] = true;
23649 }
23650 else
23651 {
23652 // Mark the element as not inverted
23653 is_inverted[tmp_ele_pt] = false;
23654 }
23655 // Set the flag to indicate a new element was added
23656 new_element_added = true;
23657 // Set the flag to indicate the element was added to
23658 // the left
23659 element_added_to_the_left = true;
23660 }
23661 // Check if the elements goes to the right
23662 else if (final_node_pt == first_node_pt &&
23663 !connection_to_the_right)
23664 {
23665 // Update the final node and the just added node
23666 new_added_node_pt = final_node_pt = last_node_pt;
23667 // Add the most right node
23668 tmp_sorted_nodes_pt.push_back(last_node_pt);
23669 // Add the face element to the list of sorted face
23670 // elements
23671 tmp_sorted_face_ele_pt.push_back(tmp_ele_pt);
23672 // Add the bulk element to the list of sorted elements
23673 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23674 // Add the face index to the list of sorted face index
23675 // elements
23676 tmp_sorted_face_index_ele.push_back(
23677 unsorted_face_index_ele[iproc][e]);
23678 if (!treat_as_inverted[iproc][e])
23679 {
23680 // Mark the element as not inverted
23681 is_inverted[tmp_ele_pt] = false;
23682 }
23683 else
23684 {
23685 // Mark the element as inverted
23686 is_inverted[tmp_ele_pt] = true;
23687 }
23688 // Set the flag to indicate a new element was added
23689 new_element_added = true;
23690 // Set the flag to indicate the element was added to
23691 // the right
23692 element_added_to_the_right = true;
23693 }
23694 // Check if the elements goes to the right (but inverted)
23695 else if (final_node_pt == last_node_pt &&
23696 !connection_to_the_right)
23697 {
23698 // Update the final node and the just added node
23699 new_added_node_pt = final_node_pt = first_node_pt;
23700 // Add the most right node
23701 tmp_sorted_nodes_pt.push_back(first_node_pt);
23702 // Add the face element to the list of sorted face
23703 // elements
23704 tmp_sorted_face_ele_pt.push_back(tmp_ele_pt);
23705 // Add the bulk element to the list of sorted elements
23706 tmp_sorted_ele_pt.push_back(unsorted_ele_pt[iproc][e]);
23707 // Add the face index to the list of sorted face index
23708 // elements
23709 tmp_sorted_face_index_ele.push_back(
23710 unsorted_face_index_ele[iproc][e]);
23711 if (!treat_as_inverted[iproc][e])
23712 {
23713 // Mark the element as inverted
23714 is_inverted[tmp_ele_pt] = true;
23715 }
23716 else
23717 {
23718 // Mark the element as not inverted
23719 is_inverted[tmp_ele_pt] = false;
23720 }
23721 // Set the flag to indicate a new elements was added
23722 new_element_added = true;
23723 // Set the flag to indicate the element was added to
23724 // the right
23725 element_added_to_the_right = true;
23726 }
23727
23728 // Do additional stuff if the element was added
23729 if (new_element_added)
23730 {
23731 // Mark the element as done
23732 done_ele[tmp_ele_pt] = true;
23733 // Increase the counter for sorted face elements
23734 nsorted_face_ele++;
23735
23736 // Get the global degree of the node (notice the
23737 // local degree has been updated to global degree)
23738 const unsigned new_added_node_degree =
23739 global_node_degree[new_added_node_pt];
23740
23741 // Based on which side the element was added, look for
23742 // connections on that side
23743
23744 // Verify for connections to the left (we need to
23745 // check for the connection variable too, since
23746 // after a connection has been done we no longer
23747 // need to verify for this condition)
23748 if (element_added_to_the_left && !connection_to_the_left)
23749 {
23750 // Check for connection
23751 bound_id_connection_to_the_left =
23752 this->check_connections_of_polyline_nodes(
23753 element_in_processor_pt,
23754 root_edge_bound_id,
23755 overlapped_edge,
23756 node_on_bnd_not_overlapped_by_shd_bnd,
23757 tmp_sorted_nodes_pt,
23758 local_shd_bnd_id_to_sorted_list_node_pt,
23759 new_added_node_degree,
23760 new_added_node_pt,
23761 called_for_load_balance);
23762
23763 // If there is a stop condition then set the
23764 // corresponding flag
23765 if (bound_id_connection_to_the_left != -1)
23766 {
23767 connection_to_the_left = true;
23768 } // if (bound_id_connection_to_the_left != -1)
23769
23770 } // if (node_added_to_the_left &&
23771 // !connection_to_the_left)
23772
23773 // Verify for connections to the right (we need to
23774 // check for the connection variable too, since
23775 // after a connection has been done we no longer
23776 // need to verify for this condition)
23777 if (element_added_to_the_right && !connection_to_the_right)
23778 {
23779 // Check for connection
23780 bound_id_connection_to_the_right =
23781 this->check_connections_of_polyline_nodes(
23782 element_in_processor_pt,
23783 root_edge_bound_id,
23784 overlapped_edge,
23785 node_on_bnd_not_overlapped_by_shd_bnd,
23786 tmp_sorted_nodes_pt,
23787 local_shd_bnd_id_to_sorted_list_node_pt,
23788 new_added_node_degree,
23789 new_added_node_pt,
23790 called_for_load_balance);
23791
23792 // If there is a stop condition then set the
23793 // corresponding flag
23794 if (bound_id_connection_to_the_right != -1)
23795 {
23796 connection_to_the_right = true;
23797 } // if (bound_id_connection_to_the_right != -1)
23798
23799 } // if (node_added_to_the_right &&
23800 // !connection_to_the_right)
23801
23802 // If the current shared boundary has connections at
23803 // both ends then stop the adding of elements (and
23804 // nodes)
23805 if (connection_to_the_left && connection_to_the_right)
23806 {
23807 current_polyline_has_connections_at_both_ends = true;
23808 }
23809
23810 // Break the for (looping over unsorted face
23811 // elements) and re-start looking for more elements
23812 // that fit to the left or right
23813 break;
23814
23815 } // if (new_element_added)
23816
23817 } // if (!done_ele[tmp_ele_pt])
23818
23819 } // for (e < nunsorted_face_ele)
23820
23821 } // while(new_element_added &&
23822 // (nsorted_face_ele < nunsorted_face_ele)
23823 // && !current_polyline_has_connections_at_both_ends)
23824
23825 // ------------------------------------------------------------
23826 // Before assigning a local shared boundary id to the list of
23827 // nodes and boundary elements, check for any loop that the
23828 // shared boundary may be creating
23829
23830 // The vector of the elements
23831 Vector<FiniteElement*> tmp_vector_sorted_ele_pt;
23832 // Store the list of elements on a vector of elements
23833 for (std::list<FiniteElement*>::iterator it =
23834 tmp_sorted_ele_pt.begin();
23835 it != tmp_sorted_ele_pt.end();
23836 it++)
23837 {
23838 tmp_vector_sorted_ele_pt.push_back((*it));
23839 }
23840
23841 // The vector of the face elements
23842 Vector<FiniteElement*> tmp_vector_sorted_face_ele_pt;
23843 // Store the list of face elements on a vector of face
23844 // elements
23845 for (std::list<FiniteElement*>::iterator it =
23846 tmp_sorted_face_ele_pt.begin();
23847 it != tmp_sorted_face_ele_pt.end();
23848 it++)
23849 {
23850 tmp_vector_sorted_face_ele_pt.push_back((*it));
23851 }
23852
23853 // The vector of the face indexes
23854 Vector<int> tmp_vector_sorted_face_index_ele;
23855 // Store the list of elements on a vector of elements
23856 for (std::list<int>::iterator it = tmp_sorted_face_index_ele.begin();
23857 it != tmp_sorted_face_index_ele.end();
23858 it++)
23859 {
23860 tmp_vector_sorted_face_index_ele.push_back((*it));
23861 }
23862
23863 // Store the nodes for the new shared polylines without loops
23864 Vector<std::list<Node*>> final_sorted_nodes_pt;
23865 // Store the boundary elements of the shared polyline without
23866 // loops
23867 Vector<Vector<FiniteElement*>> final_boundary_element_pt;
23868 // Store the boundary face elements of the shared polyline
23869 // without loops
23870 Vector<Vector<FiniteElement*>> final_boundary_face_element_pt;
23871 // Face indexes of the boundary elements without loops
23872 Vector<Vector<int>> final_face_index_element;
23873 // Connection flags (to the left) of the shared boundaries
23874 // without loops
23875 Vector<int> final_bound_id_connection_to_the_left;
23876 // Connection flags (to the right) of the shared boundaries
23877 // without loops
23878 Vector<int> final_bound_id_connection_to_the_right;
23879
23880 // Break any possible loop created by the shared polyline
23881 this->break_loops_on_shared_polyline_load_balance_helper(
23882 local_shd_bnd_id,
23883 tmp_sorted_nodes_pt,
23884 tmp_vector_sorted_ele_pt,
23885 tmp_vector_sorted_face_ele_pt,
23886 tmp_vector_sorted_face_index_ele,
23887 bound_id_connection_to_the_left,
23888 bound_id_connection_to_the_right,
23889 final_sorted_nodes_pt,
23890 final_boundary_element_pt,
23891 final_boundary_face_element_pt,
23892 final_face_index_element,
23893 final_bound_id_connection_to_the_left,
23894 final_bound_id_connection_to_the_right);
23895
23896 // Get the number of final sorted nodes
23897 const unsigned n_final_sorted_nodes = final_sorted_nodes_pt.size();
23898
23899 // Loop over the list of final sorted nodes
23900 for (unsigned i = 0; i < n_final_sorted_nodes; i++)
23901 {
23902 // Store the list of nodes that gave rise to the shared
23903 // boundary
23904 local_shd_bnd_id_to_sorted_list_node_pt[local_shd_bnd_id] =
23905 final_sorted_nodes_pt[i];
23906
23907 // Store the local shared boundary id assigned to the
23908 // elements that will create the shared boundary
23909 proc_local_shared_boundary_id[iproc].push_back(local_shd_bnd_id);
23910
23911 // Increase the shared boundary id (note that this is only
23912 // used to keep track of the list of nodes that create the
23913 // shared boundaries in the current processor)
23914 local_shd_bnd_id++;
23915
23916 // Include the vector of elements to the sorted vector
23917 sorted_ele_pt[iproc].push_back(final_boundary_element_pt[i]);
23918
23919 // Include the vector of face elements to the sorted vector
23920 sorted_face_ele_pt[iproc].push_back(
23921 final_boundary_face_element_pt[i]);
23922
23923 // Include the vector of elements to the sorted vector
23924 sorted_face_index_ele[iproc].push_back(final_face_index_element[i]);
23925
23926 // Include the possible associated boundary id to the vector
23927 edge_boundary_id[iproc].push_back(root_edge_bound_id);
23928
23929 // Include the connection information associated with the
23930 // current set of face elements (that will give rise to a
23931 // shared polyline
23932 // The temporal storage for the boundary connections ids
23933 Vector<int> bnd_connections_ids(2);
23934 bnd_connections_ids[0] = final_bound_id_connection_to_the_left[i];
23935 bnd_connections_ids[1] = final_bound_id_connection_to_the_right[i];
23936 sorted_connection_info[iproc].push_back(bnd_connections_ids);
23937
23938 } // for (i < n_final_sorted_nodes)
23939
23940 } // while (nsorted_face_ele < nunsorted_face_ele)
23941
23942 } // if (iproc != my_rank)
23943
23944 } // for (iproc < nproc)
23945
23946 // The time to sort shared boundaries face elements to create a
23947 // continuous representation of the boundary
23948 if (Print_timings_level_load_balance > 2)
23949 {
23950 oomph_info << "CPU for joining shared boundary face elements (load "
23951 "balance) [9.5]: "
23952 << TimingHelpers::timer() - tt_start_join_shd_bnd_face_ele
23953 << std::endl;
23954 }
23955
23956 // ==================================================================
23957 // END: SORT THE SHARED BOUNDARY FACE ELEMENTS, ADD FACE ELEMENTS TO
23958 // THE LEFT OR RIGHT OF THE ROOT FACE ELEMENT. STOP ADDING WHEN THE
23959 // MOST LEFT OR MOST RIGHT ELEMENT (NODE) IS ALREADY PART OF ANOTHER
23960 // BOUNDARY (THIS MEANS THAT THE SHARED BOUNDARY THAT IS BEING
23961 // CREATED HAS A CONNECTION). ALSO REMEMBER TO CHECK FOR THE CASE
23962 // WHEN THE MOST LEFT OR MOST RIGHT NODE IS A BOUNDARY NODE OF A
23963 // BOUNDARY THAT NO LONGER EXIST IN THE DOMAIN. AT THE END OF THIS
23964 // SECTION WE WILL HAVE THE NUMBER OF SHARED BOUNDARIES OF THIS
23965 // PROCESSOR WITH OTHERS BUT NOT THE GLOBAL SHARED BOUNDARY ID
23966 // ==================================================================
23967
23968 // ==================================================================
23969 // BEGIN: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE
23970 // NUMBER OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT
23971 // PROCESSOR IS IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF
23972 // SHARED BOUNDARIES HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE
23973 // ROOT PROCESSOR COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID
23974 // BETWEEN EACH PAIR OR PROCESSORS AND SENDS THESE INFO. TO ALL
23975 // PROCESSORS. THE GLOBAL INITIAL AND FINAL SHARED BOUNDARY ID ARE
23976 // ALSO COMPUTED
23977 // ==================================================================
23978
23979 // Get the time to compute new shared boundaries ids
23980 double tt_start_get_new_shared_boundaries_ids = 0.0;
23981 if (Print_timings_level_load_balance > 2)
23982 {
23983 tt_start_get_new_shared_boundaries_ids = TimingHelpers::timer();
23984 }
23985
23986 // Get the number of shared boundaries with in each processor
23987 Vector<unsigned> nshared_boundaries_with_processor(nproc);
23988 // Loop over the processors
23989 for (unsigned iproc = 0; iproc < nproc; iproc++)
23990 {
23991 // No shared boundaries with myself
23992 if (iproc != my_rank)
23993 {
23994 // Store the number of shared boundaries of the current
23995 // processor (my_rank) with the iproc processor
23996 nshared_boundaries_with_processor[iproc] =
23997 sorted_face_ele_pt[iproc].size();
23998
23999 } // if (iproc != my_rank)
24000
24001 } // for (iproc < nproc)
24002
24003 // Each processor sends the number of shared boundaries that it has
24004 // with in each other processor to the "root_processor" which will
24005 // be in charge of checking and computing the global shared
24006 // boundaries ids
24007 const unsigned root_processor = 0;
24008
24009 // Get the communicator of the mesh
24010 OomphCommunicator* comm_pt = this->communicator_pt();
24011
24012 // Container where to store the info. received from other processor
24013 // in root. It receives from all processors the number of shared
24014 // boundaries that each one has with any other processor
24015 Vector<unsigned> flat_unsigned_root_received_data(nproc * nproc);
24016
24017 // Gather the info. in the "root_processor"
24018 MPI_Gather(&nshared_boundaries_with_processor[0], // Info. sent from
24019 // each processor
24020 nproc, // Total number of data to send from each
24021 // processor
24022 MPI_UNSIGNED,
24023 &flat_unsigned_root_received_data[0], // Container where
24024 // to receive the
24025 // info. from all
24026 // the processors
24027 nproc, // Number of data to receive from each processor
24028 MPI_UNSIGNED,
24029 root_processor, // The processor that receives all the
24030 // info.
24031 comm_pt->mpi_comm());
24032
24033 // Container where root store the info. that will be sent back to
24034 // all processor, because root performs a Broadcast operation then
24035 // the info. is received in the same container
24036 Vector<unsigned> flat_unsigned_root_send_receive_data;
24037
24038 // Compute the new initial and final shared boundary id (they are
24039 // based on the global number of shared boundaries)
24040 unsigned new_initial_shared_boundary_id = 0;
24041 unsigned new_final_shared_boundary_id = 0;
24042
24043 // Compute the boundaries ids for the shared boundaries
24044 if (my_rank == root_processor)
24045 {
24046 // Change the representation of the data received from all
24047 // processors to a matrix representation for ease access
24048 Vector<Vector<unsigned>> root_nshared_bound_proc_with_proc(nproc);
24049 // Loop over the processors and get the number of shared
24050 // boundaries of processor iproc with jproc
24051 for (unsigned iproc = 0; iproc < nproc; iproc++)
24052 {
24053 // Resize the vector to store the data
24054 root_nshared_bound_proc_with_proc[iproc].resize(nproc);
24055 // Loop over the processors and get the number of shared
24056 // boundaries of processor iproc with jproc
24057 for (unsigned jproc = 0; jproc < nproc; jproc++)
24058 {
24059 root_nshared_bound_proc_with_proc[iproc][jproc] =
24060 flat_unsigned_root_received_data[(iproc * nproc) + jproc];
24061
24062 } // for (jproc < nproc)
24063
24064 } // for (iproc < nproc)
24065
24066#ifdef PARANOID
24067 // Check that the same number of boundaries are shared by two
24068 // specific processors
24069 for (unsigned iproc = 0; iproc < nproc; iproc++)
24070 {
24071 for (unsigned jproc = 0; jproc < iproc; jproc++)
24072 {
24073 if (root_nshared_bound_proc_with_proc[iproc][jproc] !=
24074 root_nshared_bound_proc_with_proc[jproc][iproc])
24075 {
24076 std::ostringstream error_stream;
24077 error_stream
24078 << "ROOT PROCESSOR ERROR\n\n"
24079 << "The number of shared boundaries between processor (" << iproc
24080 << ") and (" << jproc << ") is not the same:\n"
24081 << "Shared boundaries of processor (" << iproc
24082 << ") with processor (" << jproc << "): ("
24083 << root_nshared_bound_proc_with_proc[iproc][jproc] << ")\n"
24084 << "Shared boundaries of processor (" << jproc
24085 << ") with processor (" << iproc << "): ("
24086 << root_nshared_bound_proc_with_proc[jproc][iproc] << ")\n\n";
24087 throw OomphLibError(error_stream.str(),
24088 OOMPH_CURRENT_FUNCTION,
24089 OOMPH_EXCEPTION_LOCATION);
24090
24091 } // The number of shared boundaries between processors
24092 // "iproc" and "jproc" is not the same
24093
24094 } // for (jproc < iproc)
24095
24096 } // for (iproc < nproc)
24097#endif
24098
24099 // The enumeration of the shared boundaries starts from the lowest
24100 // processor number to the highest processor number
24101
24102 // Two processors share the same boundaries ids, the lowest
24103 // processor number is the one in charge of computing the shared
24104 // boundaries ids
24105 Vector<Vector<unsigned>> start_shared_bound_id_proc_with_proc(nproc);
24106 // Resize the vector, we can not do it when storing the
24107 // info. because of the strategy to save the info.
24108 for (unsigned iproc = 0; iproc < nproc; iproc++)
24109 {
24110 start_shared_bound_id_proc_with_proc[iproc].resize(nproc);
24111 }
24112
24113 // The shared boundaries ids start from the current number of
24114 // original boundaries
24115 unsigned shared_bound_id = this->nboundary();
24116
24117 // Set the new initial shared boundary id
24118 new_initial_shared_boundary_id = shared_bound_id;
24119
24120 // Assign the global shared boundary id for the shared boundaries
24121 for (unsigned iproc = 0; iproc < nproc; iproc++)
24122 {
24123 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24124 {
24125 // Are there shared boundaries between the pair of processors
24126 if (root_nshared_bound_proc_with_proc[iproc][jproc] > 0)
24127 {
24128 // Set the start boundary id of processor "iproc" with
24129 // processor "jproc" and viceversa
24130 start_shared_bound_id_proc_with_proc[iproc][jproc] =
24131 shared_bound_id;
24132 start_shared_bound_id_proc_with_proc[jproc][iproc] =
24133 shared_bound_id;
24134 // Increase the shared boundary id counter with as many
24135 // shared boundaries there are between the iproc and jproc
24136 // processor
24137 shared_bound_id += root_nshared_bound_proc_with_proc[iproc][jproc];
24138 } // if (root_nshared_bound_proc_with_proc[iproc][jproc] > 0)
24139
24140 } // for (jproc < iproc)
24141
24142 } // for (iproc < nproc)
24143
24144 // Set the new final shared boundary id
24145 new_final_shared_boundary_id = shared_bound_id;
24146
24147 // Prepare the info. to send back to each processor
24148 Vector<unsigned> send_start_shared_bound_id_proc_with_proc(nproc * nproc);
24149
24150 // Copy the info. to the storage to send the info. back to other
24151 // processors
24152 for (unsigned iproc = 0; iproc < nproc; iproc++)
24153 {
24154 for (unsigned jproc = 0; jproc < nproc; jproc++)
24155 {
24156 // Get the initial shared boundary id between each pair of
24157 // processors (iproc, jproc)
24158 const unsigned initial_shd_bnd_id =
24159 start_shared_bound_id_proc_with_proc[iproc][jproc];
24160 flat_unsigned_root_send_receive_data.push_back(initial_shd_bnd_id);
24161
24162 // .. then copy the number of shared boundaries that there are
24163 // between processor iproc and jproc
24164 const unsigned nshared_bnd_iproc_jproc =
24165 root_nshared_bound_proc_with_proc[iproc][jproc];
24166 flat_unsigned_root_send_receive_data.push_back(
24167 nshared_bnd_iproc_jproc);
24168
24169 } // for (jproc < nproc)
24170
24171 } // for (iproc < nproc)
24172
24173 // .. at the end of the data to send include the global initial
24174 // shared boundary id
24175 flat_unsigned_root_send_receive_data.push_back(
24176 new_initial_shared_boundary_id);
24177
24178 // ... and the global final shared boundary id
24179 flat_unsigned_root_send_receive_data.push_back(
24180 new_final_shared_boundary_id);
24181
24182 } // if (my_rank == root_processor)
24183
24184 // Send the initial shared boundaries ids and the number of shared
24185 // boundaries between all procesors to all processors. All
24186 // processors need to know this info.
24187
24188 // The number of data that will be sent by root to other processors
24189 // and the number of data that other processors receive from root,
24190 // it is the same because it is performed via a Broadcast
24191 unsigned root_ndata_sent_to_all_proc =
24192 flat_unsigned_root_send_receive_data.size();
24193
24194 MPI_Bcast(&root_ndata_sent_to_all_proc, // Data to send
24195 1,
24196 MPI_UNSIGNED,
24197 root_processor,
24198 comm_pt->mpi_comm());
24199
24200 // Resize the container if this is a processor that receives data
24201 if (my_rank != root_processor)
24202 {
24203 flat_unsigned_root_send_receive_data.resize(root_ndata_sent_to_all_proc);
24204 }
24205
24206 // Send back the start boundaries ids for the shared boundaries
24207 // Scatter the info. from the "root_processor"
24208 MPI_Bcast(&flat_unsigned_root_send_receive_data[0], // Info. sent to
24209 // each
24210 // processor
24211 root_ndata_sent_to_all_proc, // Total number of data to
24212 // send to each processor
24213 MPI_UNSIGNED,
24214 root_processor, // The processor that sends all the info.
24215 comm_pt->mpi_comm());
24216
24217 // The container to store the initial shared boundaries ids between
24218 // each pair of processors
24219 Vector<Vector<unsigned>> initial_shared_bound_id_proc_with_proc(nproc);
24220
24221 // All processors need to know how many shared boundaries there are
24222 // between each pair of processors
24223
24224 // The number of shared boundaries between each pair of processors
24225 Vector<Vector<unsigned>> nshared_bound_proc_with_proc(nproc);
24226
24227 unsigned iflat_counter = 0;
24228 // Fill the containers with the received info. from root processor
24229 for (unsigned iproc = 0; iproc < nproc; iproc++)
24230 {
24231 // Resize the containers
24232 initial_shared_bound_id_proc_with_proc[iproc].resize(nproc);
24233 nshared_bound_proc_with_proc[iproc].resize(nproc);
24234
24235 // Loop over the processors
24236 for (unsigned jproc = 0; jproc < nproc; jproc++)
24237 {
24238 // Get the initial shared boundary id between each pair of
24239 // processors (iproc, jproc)
24240 initial_shared_bound_id_proc_with_proc[iproc][jproc] =
24241 flat_unsigned_root_send_receive_data[iflat_counter++];
24242
24243 // .. and copy the number of shared boundaries that there are
24244 // between processor iproc and jproc
24245 nshared_bound_proc_with_proc[iproc][jproc] =
24246 flat_unsigned_root_send_receive_data[iflat_counter++];
24247
24248 } // for (jproc < nproc)
24249
24250 } // for (iproc < nproc)
24251
24252 // Read the new initial shared boundary id
24253 new_initial_shared_boundary_id =
24254 flat_unsigned_root_send_receive_data[root_ndata_sent_to_all_proc - 2];
24255
24256 // Read the new final shared boundary id
24257 new_final_shared_boundary_id =
24258 flat_unsigned_root_send_receive_data[root_ndata_sent_to_all_proc - 1];
24259
24260 // The time to compute new shared boundaries ids
24261 if (Print_timings_level_load_balance > 2)
24262 {
24264 << "CPU for computing new shared boundaries ids (load balance) [9.6]: "
24265 << TimingHelpers::timer() - tt_start_get_new_shared_boundaries_ids
24266 << std::endl;
24267 }
24268
24269 // ==================================================================
24270 // END: COMPUTE THE GLOBAL SHARED BOUNDARIES IDS. GATHER THE NUMBER
24271 // OF SHARED BOUNDARIES OF EACH PROCESSOR, THEN A ROOT PROCESSOR IS
24272 // IN CHARGE OF VERIFYING THAT THE SAME NUMBER OF SHARED BOUNDARIES
24273 // HAVE BEEN CREATED BY A PAIR OF PROCESSORS. THE ROOT PROCESSOR
24274 // COMPUTES THE INITIAL GLOBAL SHARED BOUNDARY ID BETWEEN EACH PAIR
24275 // OR PROCESSORS AND SENDS THESE INFO. TO ALL PROCESSORS. THE GLOBAL
24276 // INITIAL AND FINAL SHARED BOUNDARY ID ARE ALSO COMPUTED
24277 // ==================================================================
24278
24279 // ==================================================================
24280 // BEGIN: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24281 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24282 // SHARED BOUNDARIES INFO.
24283 // ==================================================================
24284
24285 // Get the time to create new shared boundaries representations
24286 double tt_start_create_new_shared_boundaries_polylines = 0.0;
24287 if (Print_timings_level_load_balance > 2)
24288 {
24289 tt_start_create_new_shared_boundaries_polylines = TimingHelpers::timer();
24290 }
24291
24292 // Create the shared boundaries and establish all the related info.
24293 // - Create Polylines
24294 // - Store shared boundary elements
24295 // - Fill data structures to know which shared boundaries belong to
24296 // which processor
24297
24298 // Resize the shared polylines container
24299 this->flush_shared_boundary_polyline_pt();
24300 this->Shared_boundary_polyline_pt.resize(nproc);
24301
24302 // Resize for the boundaries ids shared with all processors
24303 this->Shared_boundaries_ids.clear();
24304 this->Shared_boundaries_ids.resize(nproc);
24305 for (unsigned iproc = 0; iproc < nproc; iproc++)
24306 {
24307 this->Shared_boundaries_ids[iproc].clear();
24308 this->Shared_boundaries_ids[iproc].resize(nproc);
24309 } // for (iproc < nproc)
24310
24311 // Clear data
24312 this->Shared_boundary_from_processors.clear();
24313 this->Shared_boundary_overlaps_internal_boundary.clear();
24314 this->Boundary_was_splitted.clear();
24315 this->Boundary_subpolylines.clear();
24316 this->Boundary_marked_as_shared_boundary.clear();
24317
24318 // Flush data
24319 this->flush_shared_boundary_element();
24320 this->flush_face_index_at_shared_boundary();
24321 this->flush_shared_boundary_node();
24322 this->flush_sorted_shared_boundary_node();
24323
24324 // Store the old local inital shared boundary id (used to map from
24325 // local shared boundary id to global shared boundary id)
24326 const unsigned old_local_shd_bnd_id = this->Initial_shared_boundary_id;
24327
24328 // Update the initial and final shared boundary id
24329 this->Initial_shared_boundary_id = new_initial_shared_boundary_id;
24330 this->Final_shared_boundary_id = new_final_shared_boundary_id;
24331
24332 // Storage for the new created polylines between the current
24333 // processor (my_rank) and the other processors, unsorted polylines
24334 Vector<TriangleMeshPolyLine*> unsorted_polylines_pt;
24335
24336 // Map to get the global shared boundary id from the local shared
24337 // boundary id. Note that this is only used to get the global shared
24338 // boundary id when the shared boundary that is being created has
24339 // connections
24340 std::map<unsigned, unsigned> local_to_global_shd_bnd_id;
24341
24342 // Each processor knows the boundaries ids for each of the shared
24343 // boundaries it has, establish that info. in the proper containers
24344 // Additionally, store the shared boundaries of ALL processors with
24345 // ALL processors, but only create the shared boundaries (and their
24346 // respective polylines) of the current processor (my_rank)
24347 for (unsigned iproc = 0; iproc < nproc; iproc++)
24348 {
24349 // Avoid creating double shared boundaries, the shared boundaries
24350 // created between processor "iproc" and processor "jproc" are the
24351 // same than those created between processor "jproc" and processor
24352 // "iproc"
24353 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
24354 {
24355 // If we are working with the current processor (my_rank) then
24356 // create the shared boundaries, if that is not the case then
24357 // only fill the info. on the proper containers
24358 if (iproc == my_rank || jproc == my_rank)
24359 {
24360 // Check the condition that made it get here
24361 unsigned ref_proc = 0;
24362 if (iproc == my_rank)
24363 {
24364 ref_proc = jproc;
24365 }
24366 else if (jproc == my_rank)
24367 {
24368 ref_proc = iproc;
24369 }
24370
24371 // Get the number of shared boundaries between processor iproc
24372 // and processor jproc
24373 const unsigned nshared_bound_iproc_jproc =
24374 nshared_bound_proc_with_proc[iproc][jproc];
24375
24376 // Loop over the number of shared boundaries
24377 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24378 counter++)
24379 {
24380 // Compute the shared boundary id for the shared boundary
24381 const unsigned shd_bnd_id =
24382 initial_shared_bound_id_proc_with_proc[iproc][jproc] + counter;
24383 // Set up the shared boundaries between "iproc" (my_rank)
24384 // and "jproc"
24385 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24386 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24387
24388 // Specify the processors involved for the creation of the
24389 // shared boundary
24390 Vector<unsigned> processors(2);
24391 processors[0] = iproc;
24392 processors[1] = jproc;
24393 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24394
24395 // Get the possible root edge id associated to the shared
24396 // boundary (useful when the shared boundary overlaps an
24397 // original boundary)
24398 int root_edge_bound_id = edge_boundary_id[ref_proc][counter];
24399 // Check if the shared boundary is overlapping (or is part)
24400 // of an internal boundary
24401 if (root_edge_bound_id != -1)
24402 {
24403 // If the shared boundary is part of an internal boundary then
24404 // mark the shared boundary
24405 this->Shared_boundary_overlaps_internal_boundary[shd_bnd_id] =
24406 static_cast<unsigned>(root_edge_bound_id);
24407 } // if (root_edge_bound_id != -1)
24408
24409 // Storing for the nodes of the polyline (these are different
24410 // from the nodes on the face elements -- it is actually a
24411 // sub-set -- since the polyline is created from the first and
24412 // last nodes on the face elements)
24413 Vector<Node*> node_pt_to_create_shared_polyline;
24414
24415 // Add the first node for the very first face element. In
24416 // the loop we will only add the last node of the face
24417 // element
24418 FiniteElement* first_face_ele_pt =
24419 sorted_face_ele_pt[ref_proc][counter][0];
24420
24421 // Get the number of nodes on the first face element
24422 const unsigned first_face_ele_nnodes = first_face_ele_pt->nnode();
24423 if (!is_inverted[first_face_ele_pt])
24424 {
24425 // Get the first node
24426 Node* first_node_pt = first_face_ele_pt->node_pt(0);
24427 // Add the node to create the polyline
24428 node_pt_to_create_shared_polyline.push_back(first_node_pt);
24429 // Add the first node to the shared boundary
24430 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24431 }
24432 else
24433 {
24434 // Get the first node in the inverted face element
24435 Node* first_node_pt =
24436 first_face_ele_pt->node_pt(first_face_ele_nnodes - 1);
24437 // Add the node to create the polyline
24438 node_pt_to_create_shared_polyline.push_back(first_node_pt);
24439 // Add the first node to the shared boundary
24440 this->add_shared_boundary_node(shd_bnd_id, first_node_pt);
24441 }
24442
24443 // ... and extract only the last nodes of the face elements
24444 // in the next loop and add them in the vector of nodes to
24445 // create polylines (node_pt_to_create_shared_polyline)
24446
24447 // Get the number of elements
24448 const unsigned nshared_boundary_elements =
24449 sorted_face_ele_pt[ref_proc][counter].size();
24450
24451 // Store the shared boundary elements, nodes and get the
24452 // sorted nodes to create the polyline
24453 for (unsigned ie = 0; ie < nshared_boundary_elements; ie++)
24454 {
24455 // Get the bulk element version of the face element
24456 FiniteElement* bulk_ele_pt = sorted_ele_pt[ref_proc][counter][ie];
24457
24458 // Add the shared boundary element and associate it to the
24459 // "shd_bnd_id"
24460 this->add_shared_boundary_element(shd_bnd_id, bulk_ele_pt);
24461
24462 // Get the face index from which the face element was
24463 // created from the bulk element
24464 const int face_index =
24465 sorted_face_index_ele[ref_proc][counter][ie];
24466
24467 // Add the face index to the face indexes of the shared
24468 // boundary
24469 this->add_face_index_at_shared_boundary(shd_bnd_id, face_index);
24470
24471 // Get the face element to obtain the last node
24472 FiniteElement* face_ele_pt =
24473 sorted_face_ele_pt[ref_proc][counter][ie];
24474
24475 // Get the number of nodes
24476 const unsigned nnodes = face_ele_pt->nnode();
24477 if (!is_inverted[face_ele_pt])
24478 {
24479 // We have already added the first node, then start from
24480 // the second one
24481 for (unsigned n = 1; n < nnodes; n++)
24482 {
24483 // Get the node to be added
24484 Node* node_pt = face_ele_pt->node_pt(n);
24485 // Add the node and associate it to the shared boundary
24486 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24487 } // for (n < nnodes)
24488
24489 // Add the last node of the face element to the vector of
24490 // nodes to create the polyline
24491 // Get the last node
24492 Node* last_node_pt = face_ele_pt->node_pt(nnodes - 1);
24493 node_pt_to_create_shared_polyline.push_back(last_node_pt);
24494 } // if (!is_inverted[face_ele_pt])
24495 else
24496 {
24497 // We have already added the first node, then start from
24498 // the second one (in reverse order)
24499 for (int n = nnodes - 2; n >= 0; n--)
24500 {
24501 // Get the node to be added
24502 Node* node_pt = face_ele_pt->node_pt(n);
24503 // Add the node and associate it to the shared boundary
24504 this->add_shared_boundary_node(shd_bnd_id, node_pt);
24505 } // for (n < nnodes)
24506
24507 // Add the last node of the face element to the vector of
24508 // nodes to create the polyline
24509 // Get the last node
24510 Node* last_node_pt = face_ele_pt->node_pt(0);
24511 node_pt_to_create_shared_polyline.push_back(last_node_pt);
24512
24513 } // else if (!is_inverted[face_ele_pt])
24514
24515 } // for (ie < nshared_boundary_elements)
24516
24517 // The number of nodes for the shared boundary polyline
24518 const unsigned nnodes_to_create_shared_boundary =
24519 node_pt_to_create_shared_polyline.size();
24520
24521 // Get the vertices that create the shared boundary polyline
24522 Vector<Vector<double>> vertices(nnodes_to_create_shared_boundary);
24523 for (unsigned n = 0; n < nnodes_to_create_shared_boundary; n++)
24524 {
24525 vertices[n].resize(2);
24526 // Get the node
24527 Node* tmp_node_pt = node_pt_to_create_shared_polyline[n];
24528 // Get the vertices
24529 vertices[n][0] = tmp_node_pt->x(0);
24530 vertices[n][1] = tmp_node_pt->x(1);
24531 } // for (n < nnodes_to_create_shared_boundary)
24532
24533 // Create the polyline
24534 TriangleMeshPolyLine* polyline_pt =
24535 new TriangleMeshPolyLine(vertices, shd_bnd_id);
24536
24537 // Updates bnd_id<--->curve section map
24538 this->Boundary_curve_section_pt[shd_bnd_id] = polyline_pt;
24539
24540 // Add the new created polyline to the list of unsorted
24541 // polylines
24542 unsorted_polylines_pt.push_back(polyline_pt);
24543
24544 // Mark the polyline for deletion (when calling destructor)
24545 this->Free_curve_section_pt.insert(polyline_pt);
24546
24547 // Now assign the connection information
24548 // ---------------------------------------------------------
24549 // Get the local shared boundary id associated to the
24550 // elements that gave rise to this shared boundary
24551 const unsigned local_shd_bnd_id =
24552 proc_local_shared_boundary_id[ref_proc][counter];
24553
24554 // Associate the local shared boundary to the global shared
24555 // boundary
24556 local_to_global_shd_bnd_id[local_shd_bnd_id] = shd_bnd_id;
24557
24558 // Get the correct shared boundaries ids, from the local
24559 // shared boundaries ids established at the identification
24560 // of the conections
24561
24562 // Get the local bnd id for the connection to the left
24563 int tmp_bnd_id_connection_to_the_left =
24564 sorted_connection_info[ref_proc][counter][0];
24565 // Get the local bnd id for the connection to the right
24566 int tmp_bnd_id_connection_to_the_right =
24567 sorted_connection_info[ref_proc][counter][1];
24568
24569 // The global shared boundaries ids for connections to the
24570 // left or right
24571 int bnd_id_connection_to_the_left = -1;
24572 int bnd_id_connection_to_the_right = -1;
24573
24574 // To the left
24575 // --------------
24576
24577 // If the connection is with the same shared boundary then
24578 // set the current boundary id
24579 if (tmp_bnd_id_connection_to_the_left == -2)
24580 {
24581 // Set the current shared boundary id
24582 bnd_id_connection_to_the_left = shd_bnd_id;
24583 } // if (tmp_bnd_id_connection_to_the_left == -2)
24584
24585 // Check if the connection was a stop adding nodes condition
24586 if (tmp_bnd_id_connection_to_the_left == -3)
24587 {
24588 // Set as no connected
24589 bnd_id_connection_to_the_left = -1;
24590 } // if (tmp_bnd_id_connection_to_the_left == -3)
24591
24592 // There is a connection with another boundary, check if it
24593 // is a shared boundary or an original boundary
24594 if (tmp_bnd_id_connection_to_the_left >=
24595 static_cast<int>(old_local_shd_bnd_id))
24596 {
24597 // The connection is with a shared boundary, get the
24598 // global shared boundary id and set the connection
24599#ifdef PARANOID
24600 std::map<unsigned, unsigned>::iterator it =
24601 local_to_global_shd_bnd_id.find(
24602 static_cast<unsigned>(tmp_bnd_id_connection_to_the_left));
24603 // If the global shared boundary id was not found we
24604 // are in trouble
24605 if (it == local_to_global_shd_bnd_id.end())
24606 {
24607 std::stringstream error_message;
24608 error_message
24609 << "The global shared boundary id was not found for\n"
24610 << "the local shared boundary shared with processor ("
24611 << ref_proc << ").\n"
24612 << "This processor: (" << my_rank << ")\n"
24613 << "Boundary shared with processor: (" << ref_proc << ")\n"
24614 << "Local shared boundary: ("
24615 << tmp_bnd_id_connection_to_the_left << ")\n";
24616 throw OomphLibError(error_message.str(),
24617 OOMPH_CURRENT_FUNCTION,
24618 OOMPH_EXCEPTION_LOCATION);
24619 } // if (it==local_to_global_shd_bnd_id.end())
24620#endif
24621
24622 // Get the global shared boundary id
24623 bnd_id_connection_to_the_left =
24624 local_to_global_shd_bnd_id[static_cast<unsigned>(
24625 tmp_bnd_id_connection_to_the_left)];
24626 }
24627 else
24628 {
24629 // The connection is with an original boundary, copy
24630 // the boundary id
24631 bnd_id_connection_to_the_left = tmp_bnd_id_connection_to_the_left;
24632
24633 } // else (connection with a shared boundary)
24634
24635 // To the right
24636 // --------------
24637
24638 // If the connection is with the same shared boundary then
24639 // set the current boundary id
24640 if (tmp_bnd_id_connection_to_the_right == -2)
24641 {
24642 // Set the current shared boundary id
24643 bnd_id_connection_to_the_right = shd_bnd_id;
24644 } // if (tmp_bnd_id_connection_to_the_right == -2)
24645
24646 // Check if the connection was a stop adding nodes condition
24647 if (tmp_bnd_id_connection_to_the_right == -3)
24648 {
24649 // Set as no connected
24650 bnd_id_connection_to_the_right = -1;
24651 } // if (tmp_bnd_id_connection_to_the_right == -3)
24652
24653 // There is a connection with another boundary, check if it
24654 // is a shared boundary or an original boundary
24655 if (tmp_bnd_id_connection_to_the_right >=
24656 static_cast<int>(old_local_shd_bnd_id))
24657 {
24658 // The connection is with a shared boundary, get the
24659 // global shared boundary id and set the connection
24660#ifdef PARANOID
24661 std::map<unsigned, unsigned>::iterator it =
24662 local_to_global_shd_bnd_id.find(
24663 static_cast<unsigned>(tmp_bnd_id_connection_to_the_right));
24664 // If the global shared boundary id was not found we
24665 // are in trouble
24666 if (it == local_to_global_shd_bnd_id.end())
24667 {
24668 std::stringstream error_message;
24669 error_message
24670 << "The global shared boundary id was not found for\n"
24671 << "the local shared boundary shared with processor ("
24672 << ref_proc << ").\n"
24673 << "This processor: (" << my_rank << ")\n"
24674 << "Boundary shared with processor: (" << ref_proc << ")\n"
24675 << "Local shared boundary: ("
24676 << tmp_bnd_id_connection_to_the_right << ")\n";
24677 throw OomphLibError(error_message.str(),
24678 OOMPH_CURRENT_FUNCTION,
24679 OOMPH_EXCEPTION_LOCATION);
24680 } // if (it==local_to_global_shd_bnd_id.end())
24681#endif
24682 // Get the global shared boundary id
24683 bnd_id_connection_to_the_right =
24684 local_to_global_shd_bnd_id[static_cast<unsigned>(
24685 tmp_bnd_id_connection_to_the_right)];
24686 }
24687 else
24688 {
24689 // The connection is with an original boundary, copy the
24690 // boundary id
24691 bnd_id_connection_to_the_right =
24692 tmp_bnd_id_connection_to_the_right;
24693
24694 } // else (connection with a shared boundary)
24695
24696 // --------------------------------
24697 // Set the connection to the left
24698 if (bnd_id_connection_to_the_left != -1)
24699 {
24700 // Get the unsigned version of the boundary id to the left
24701 const unsigned ubnd_id_connection_to_the_left =
24702 static_cast<unsigned>(bnd_id_connection_to_the_left);
24703 // Set the initial vertex as connected
24704 polyline_pt->set_initial_vertex_connected();
24705 // Set the initial vertex connected boundary id
24706 polyline_pt->initial_vertex_connected_bnd_id() =
24707 ubnd_id_connection_to_the_left;
24708 // Set the chunk number to zero
24709 polyline_pt->initial_vertex_connected_n_chunk() = 0;
24710
24711 } // if (bnd_id_connection_to_the_left != -1)
24712
24713 // ---------------------------------
24714 // Set the connection to the right
24715 if (bnd_id_connection_to_the_right != -1)
24716 {
24717 // Get the unsigned version of the boundary id to the
24718 // right
24719 const unsigned ubnd_id_connection_to_the_right =
24720 static_cast<unsigned>(bnd_id_connection_to_the_right);
24721 // Set the final vertex as connected
24722 polyline_pt->set_final_vertex_connected();
24723 // Set the final vertex connected boundary id
24724 polyline_pt->final_vertex_connected_bnd_id() =
24725 ubnd_id_connection_to_the_right;
24726 // Set the chunk number to zero
24727 polyline_pt->final_vertex_connected_n_chunk() = 0;
24728
24729 } // if (bnd_id_connection_to_the_right != -1)
24730
24731 } // for (counter < nshared_bound_iproc_jproc)
24732
24733 } // if (iproc == my_rank || jproc == my_rank)
24734 else
24735 {
24736 // We are not working with the current processor, then we only
24737 // need to fill the containers
24738
24739 // Get the number of shared boundaries between processor iproc
24740 // and processor jproc
24741 const unsigned nshared_bound_iproc_jproc =
24742 nshared_bound_proc_with_proc[iproc][jproc];
24743 // Loop over the number of shared boundaries
24744 for (unsigned counter = 0; counter < nshared_bound_iproc_jproc;
24745 counter++)
24746 {
24747 // Compute the shared boundary id for the shared boundary
24748 const unsigned shd_bnd_id =
24749 initial_shared_bound_id_proc_with_proc[iproc][jproc] + counter;
24750
24751 // Set up the shared boundaries between "iproc" and "jproc"
24752 this->Shared_boundaries_ids[iproc][jproc].push_back(shd_bnd_id);
24753 this->Shared_boundaries_ids[jproc][iproc].push_back(shd_bnd_id);
24754
24755 // Specify the processors involved for the creation of the
24756 // shared boundary
24757 Vector<unsigned> processors(2);
24758 processors[0] = iproc;
24759 processors[1] = jproc;
24760 this->Shared_boundary_from_processors[shd_bnd_id] = processors;
24761
24762 } // for (counter < nshared_bound_iproc_jproc)
24763
24764 } // else if (iproc == my_rank || jproc == my_rank)
24765
24766 } // for (jproc < nproc)
24767
24768 } // for (iproc < nproc)
24769
24770 // Get the time to create new shared boundaries representations
24771 if (Print_timings_level_load_balance > 2)
24772 {
24773 oomph_info << "CPU for creating new shared boundaries representations "
24774 "(load balance) [9.7]: "
24776 tt_start_create_new_shared_boundaries_polylines
24777 << std::endl;
24778 }
24779
24780 // ==================================================================
24781 // END: CREATE THE NEW SHARED BOUNDARIES. DELETE THE OLD SHARED
24782 // BOUNDARIES INFORMATION. FILL THE DATA STRUCTURES WITH THE NEW
24783 // SHARED BOUNDARIES INFO.
24784 // ==================================================================
24785
24786 // ==================================================================
24787 // BEGIN: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24788 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24789 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24790 // ELEMENTS
24791 // ==================================================================
24792
24793 // Get the time to create the new shared curves
24794 double tt_start_create_new_shared_curves = 0.0;
24795 if (Print_timings_level_load_balance > 2)
24796 {
24797 tt_start_create_new_shared_curves = TimingHelpers::timer();
24798 }
24799
24800 // Sort the polylines and find if they create a contiguous open
24801 // curve
24802 if (unsorted_polylines_pt.size() > 0)
24803 {
24804 // Now that we have all the new unsorted polylines on "my_rank"x
24805 // processor it is time to sort them so they be all contiguous
24806 this->sort_polylines_helper(unsorted_polylines_pt,
24807 this->Shared_boundary_polyline_pt[my_rank]);
24808 }
24809
24810 // Free the memory allocated for the face elements
24811 for (unsigned iproc = 0; iproc < nproc; iproc++)
24812 {
24813 const unsigned nface_ele = unsorted_face_ele_pt[iproc].size();
24814 for (unsigned e = 0; e < nface_ele; e++)
24815 {
24816 delete unsorted_face_ele_pt[iproc][e];
24817 unsorted_face_ele_pt[iproc][e] = 0;
24818 } // for (e < nface_ele)
24819
24820 } // for (iproc < nproc)
24821
24822 // The time to create the new shared curves
24823 if (Print_timings_level_load_balance > 2)
24824 {
24826 << "CPU for creating the new shared curves (load balance) [9.8]: "
24827 << TimingHelpers::timer() - tt_start_create_new_shared_curves
24828 << std::endl;
24829 }
24830
24831 // ==================================================================
24832 // END: SORT THE SHARED BOUNDARIES AND CREATE SHARED CURVES (A SET
24833 // OF CONTIGUOUS SHARED POLYLINES). STORE THEM IN THE GLOBAL
24834 // CONTAINER FOR SHARED BOUNDARIES. FREE MEMORY BY DELETING FACE
24835 // ELEMENTS
24836 // ==================================================================
24837 }
24838
24839 //======================================================================
24840 // Computes the degree of the nodes on the shared boundaries, the
24841 // degree of the node is computed from the global graph created by the
24842 // shared boundaries of all processors
24843 //======================================================================
24844 template<class ELEMENT>
24846 Vector<Vector<FiniteElement*>>& unsorted_face_ele_pt,
24847 std::map<Node*, unsigned>& global_node_degree)
24848 {
24849 // Get the rank and number of processors
24850 const unsigned nproc = this->communicator_pt()->nproc();
24851 const unsigned my_rank = this->communicator_pt()->my_rank();
24852
24853 // Store a temporary sorting of the nodes, starting from the
24854 // lower-left position
24855 Vector<Vector<Node*>> tmp_sorted_shared_node_pt(nproc);
24856
24857 // Store the alias of the node, it may be shared by more than two
24858 // processors, they should know that the node is the same
24859 // [0] iproc, processor with which the current processor shared the node
24860 // [1] node #, number of node in the number of nodes shared with iproc
24861 // processor
24862 std::map<Node*, Vector<Vector<unsigned>>> node_alias;
24863
24864 // Stores the local adjacency matrix
24865 // (nproc*n_shared_nodes*n_shared_nodes)
24866 Vector<Vector<Vector<unsigned>>> local_adjacency_matrix(nproc);
24867
24868 // Sort the nodes and create the adjacency matrix of each sub-graph
24869 // created by the shared edges
24870 create_adjacency_matrix_new_shared_edges_helper(unsorted_face_ele_pt,
24871 tmp_sorted_shared_node_pt,
24872 node_alias,
24873 local_adjacency_matrix);
24874
24875 // Prepare the info. to be sent to the root processor, which will be
24876 // in charge of updating the nodes degree by combining the info. of
24877 // all the processors
24878
24879 // The flat package with the info. to send to root
24880 Vector<unsigned> package_unsigned_send_data_to_root;
24881
24882 // Encode the info. that will be sent to the root processor
24883
24884 // Loop over the temporary sorted nodes between each pair of
24885 // processors
24886 for (unsigned iproc = 0; iproc < nproc; iproc++)
24887 {
24888 // Send the processor index
24889 package_unsigned_send_data_to_root.push_back(iproc);
24890
24891 // Get the number of nodes shared between the processors
24892 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
24893
24894 // Send the number of nodes shared with the iproc processor
24895 package_unsigned_send_data_to_root.push_back(n_nodes);
24896
24897 // Loop over the nodes
24898 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
24899 {
24900 // Get the node
24901 Node* shd_node_pt = tmp_sorted_shared_node_pt[iproc][ishd];
24902
24903 // Get the alias info.
24904 Vector<Vector<unsigned>> alias_node_info = node_alias[shd_node_pt];
24905
24906 // Get the number of alias for the node
24907 const unsigned n_alias = alias_node_info.size();
24908
24909 // Send the number of alias assigned to the node
24910 package_unsigned_send_data_to_root.push_back(n_alias);
24911
24912 // Loop over the alias to include them in the package
24913 for (unsigned i = 0; i < n_alias; i++)
24914 {
24915 // Send the alias info.
24916 // The current processor
24917 package_unsigned_send_data_to_root.push_back(alias_node_info[i][0]);
24918 // The prociesso with which is shared
24919 package_unsigned_send_data_to_root.push_back(alias_node_info[i][1]);
24920 // The index of the node
24921 package_unsigned_send_data_to_root.push_back(alias_node_info[i][2]);
24922 } // for (i < n_alias)
24923
24924 } // for (ishd < n_nodes)
24925
24926 // Now send the adjacency matrix
24927 for (unsigned i = 0; i < n_nodes; i++)
24928 {
24929 for (unsigned j = 0; j < n_nodes; j++)
24930 {
24931 // Package the adjacency matrix
24932 package_unsigned_send_data_to_root.push_back(
24933 local_adjacency_matrix[iproc][i][j]);
24934
24935 } // for (j < n_nodes)
24936
24937 } // for (i < n_nodes)
24938
24939 } // for (iproc < nproc)
24940
24941 // Define the root processor
24942 const unsigned root_processor = 0;
24943
24944 // Get the communicator of the mesh
24945 OomphCommunicator* comm_pt = this->communicator_pt();
24946
24947 // Number of data send. from this processor to root processor
24948 unsigned n_unsigned_data_send_to_root =
24949 package_unsigned_send_data_to_root.size();
24950
24951 // Store the number of data to receive from each processor in root
24952 Vector<int> n_unsigned_data_received_in_root(nproc, 0);
24953
24954 // Send the number of data that each processor will send to root
24955 // Gather the info. in the "root_processor"
24956 MPI_Gather(&n_unsigned_data_send_to_root, // Info. sent from
24957 // each processor
24958 1, // Total number of data to send from each processor
24959 MPI_UNSIGNED,
24960 &n_unsigned_data_received_in_root[0], // Container where
24961 // to receive the
24962 // info. from all
24963 // the processors
24964 1, // Number of data to receive from each processor
24965 MPI_UNSIGNED,
24966 root_processor, // The processor that receives all the
24967 // info.
24968 comm_pt->mpi_comm());
24969
24970 // Compute the total number of data to receive from all processors
24971 unsigned n_unsigned_total_data_receive_in_root = 0;
24972 for (unsigned iproc = 0; iproc < nproc; iproc++)
24973 {
24974 // Add the number of data to receive from each processor
24975 n_unsigned_total_data_receive_in_root +=
24976 n_unsigned_data_received_in_root[iproc];
24977 }
24978
24979 // Compute the offsets from each processor
24980 Vector<int> root_unsigned_offsets_receive(nproc, 0);
24981 root_unsigned_offsets_receive[0] = 0;
24982 for (unsigned iproc = 1; iproc < nproc; iproc++)
24983 {
24984 // Compute the offset to store the values received from each
24985 // processor
24986 root_unsigned_offsets_receive[iproc] =
24987 root_unsigned_offsets_receive[iproc - 1] +
24988 n_unsigned_data_received_in_root[iproc - 1];
24989 }
24990
24991 // Create at least one entry so we don't get a seg fault below
24992 if (package_unsigned_send_data_to_root.size() == 0)
24993 {
24994 package_unsigned_send_data_to_root.resize(1);
24995 }
24996
24997 // Vector where to receive the data sent from each processor
24998 Vector<unsigned> package_unsigned_data_received_root(
24999 n_unsigned_total_data_receive_in_root);
25000 if (my_rank != root_processor)
25001 {
25002 // Create at least one entry so we don't get a seg fault below
25003 if (package_unsigned_data_received_root.size() == 0)
25004 {
25005 package_unsigned_data_received_root.resize(1);
25006 }
25007 } // if (my_rank!=root_processor)
25008
25009 // Gather the info. from all processors
25010 MPI_Gatherv(&package_unsigned_send_data_to_root[0], // Flat package
25011 // to send
25012 // info. from
25013 // each
25014 // processor
25015 n_unsigned_data_send_to_root, // Total number of data to
25016 // send from each
25017 // processor
25018 MPI_UNSIGNED,
25019 &package_unsigned_data_received_root[0], // Container
25020 // where to
25021 // receive the
25022 // info. from
25023 // all the
25024 // processors
25025 &n_unsigned_data_received_in_root[0], // Number of data
25026 // to receive from
25027 // each processor
25028 &root_unsigned_offsets_receive[0], // The offset to
25029 // store the
25030 // info. from each
25031 // processor
25032 MPI_UNSIGNED,
25033 root_processor, // The processor that receives all the
25034 // info.
25035 comm_pt->mpi_comm());
25036
25037 // Store the info. to be sent by root to other processors
25038 Vector<unsigned> package_unsigned_data_sent_from_root;
25039 // Total data sent to each processor from root
25040 Vector<int> n_unsigned_data_sent_from_root(nproc, 0);
25041
25042 // The root processor now has all the info. regarding the shared
25043 // nodes and the adjacency matrix of each pair of processors
25044 if (my_rank == root_processor)
25045 {
25046 // Decode the info. received from all processors
25047 // Counter to decode the info.
25048 unsigned decode_counter = 0;
25049
25050 // Store the local alias of the nodes in each processor
25051 // [x][][][][] iproc
25052 // [][x][][][] jproc
25053 // [][][x][][] inode
25054 // [][][][x][] ialias
25055 // [][][][][x] alias_data
25056 Vector<Vector<Vector<Vector<Vector<unsigned>>>>> local_node_alias(nproc);
25057 // Store the local adjacency matrix of each processor
25058 Vector<Vector<Vector<Vector<unsigned>>>> local_adjacency_matrix(nproc);
25059
25060 // Loop over all the processors
25061 for (unsigned iproc = 0; iproc < nproc; iproc++)
25062 {
25063 local_node_alias[iproc].resize(nproc);
25064
25065 // Resize the local adjacency matrix to store the info. sent
25066 // from all processors
25067 local_adjacency_matrix[iproc].resize(nproc);
25068
25069 if (n_unsigned_data_received_in_root[iproc] > 0)
25070 {
25071 // Loop over all the processors to decode the info. received
25072 // from each one
25073 for (unsigned jproc = 0; jproc < nproc; jproc++)
25074 {
25075 // Read the processor number to which the info. correspond
25076 const unsigned read_jproc =
25077 package_unsigned_data_received_root[decode_counter++];
25078
25079 // The read processor must be the same as the jproc, if that
25080 // is not the case then there is a synchronisation issue
25081 if (read_jproc != jproc)
25082 {
25083 std::ostringstream error_stream;
25084 error_stream
25085 << "The read processor is different from the jproc, this is\n"
25086 << "a synchronisation issue. The data are not read in the\n"
25087 << "sameorder as the were packaged\n"
25088 << "Read processor: (" << read_jproc << ")\n"
25089 << "Current jproc: (" << jproc << ")\n\n";
25090 throw OomphLibError(
25091 error_stream.str(),
25092 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25093 OOMPH_EXCEPTION_LOCATION);
25094 }
25095
25096 // Read the number of nodes in the shared boundaries between
25097 // iproc and jproc
25098 const unsigned read_n_shd_nodes_iproc_jproc =
25099 package_unsigned_data_received_root[decode_counter++];
25100
25101 // Resize the container
25102 local_node_alias[iproc][jproc].resize(read_n_shd_nodes_iproc_jproc);
25103
25104 // Loop over the number of nodes shared between iproc and
25105 // jproc
25106 for (unsigned ishd = 0; ishd < read_n_shd_nodes_iproc_jproc; ishd++)
25107 {
25108 // Read the number of alias of the current ishd node
25109 const unsigned read_n_alias_node_iproc_jproc =
25110 package_unsigned_data_received_root[decode_counter++];
25111
25112 // Resize the container
25113 local_node_alias[iproc][jproc][ishd].resize(
25114 read_n_alias_node_iproc_jproc);
25115
25116 for (unsigned ialias = 0; ialias < read_n_alias_node_iproc_jproc;
25117 ialias++)
25118 {
25119 // Resize the container, we know there are three data to
25120 // define the alias of a node
25121 local_node_alias[iproc][jproc][ishd][ialias].resize(3);
25122
25123 // The 1st processor with which is shared
25124 local_node_alias[iproc][jproc][ishd][ialias][0] =
25125 package_unsigned_data_received_root[decode_counter++];
25126
25127 // The 2nd processor with which is shared
25128 local_node_alias[iproc][jproc][ishd][ialias][1] =
25129 package_unsigned_data_received_root[decode_counter++];
25130
25131 // The index of the node in the interaction iproc-jproc
25132 local_node_alias[iproc][jproc][ishd][ialias][2] =
25133 package_unsigned_data_received_root[decode_counter++];
25134
25135 } // for (ialias < read_n_alias_node_iproc_jproc)
25136
25137 } // for (ishd < read_n_shd_nodes_iproc_jproc)
25138
25139 // Resize the local adjacency matrix
25140 local_adjacency_matrix[iproc][jproc].resize(
25141 read_n_shd_nodes_iproc_jproc);
25142 // Read the adjacency matrix sent to root processor
25143 for (unsigned i = 0; i < read_n_shd_nodes_iproc_jproc; i++)
25144 {
25145 // Resize the local adjacency matrix
25146 local_adjacency_matrix[iproc][jproc][i].resize(
25147 read_n_shd_nodes_iproc_jproc);
25148 for (unsigned j = 0; j < read_n_shd_nodes_iproc_jproc; j++)
25149 {
25150 // Read the adjacency matrix entry
25151 local_adjacency_matrix[iproc][jproc][i][j] =
25152 package_unsigned_data_received_root[decode_counter++];
25153 } // for (j < read_n_shd_nodes_iproc_jproc)
25154
25155 } // for (i < read_n_shd_nodes_iproc_jproc)
25156
25157 } // for (jproc < nproc)
25158
25159 } // for (iproc < nproc)
25160
25161 } // for (iproc < nproc)
25162
25163#ifdef PARANOID
25164 if (decode_counter != n_unsigned_total_data_receive_in_root)
25165 {
25166 std::ostringstream error_stream;
25167 error_stream
25168 << "The number of data decoded in root received from others\n"
25169 << "processors is different from the total number of data received\n"
25170 << "Data decoded: (" << decode_counter << ")\n"
25171 << "Data received: (" << n_unsigned_total_data_receive_in_root
25172 << ")\n\n"
25173 << "This is a synchronisation issue so you are probably sending\n"
25174 << "more or less info. than the one that is being decoded\n\n";
25175 throw OomphLibError(
25176 error_stream.str(),
25177 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25178 OOMPH_EXCEPTION_LOCATION);
25179 }
25180#endif
25181
25182 // Assign a unique id to the nodes (uses the alias information to
25183 // identify the repetition of a node in other processors). The
25184 // global node id is given by the position (index) in the global
25185 // node alias
25186
25187 // Keep track of those alias already assigned a unique id
25188 std::map<Vector<unsigned>, bool> alias_done;
25189
25190 // Store all the alias associated to each node
25191 Vector<Vector<Vector<unsigned>>> global_node_alias;
25192
25193 // Loop over all the processors
25194 for (unsigned iproc = 0; iproc < nproc; iproc++)
25195 {
25196 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25197 {
25198 // Read the number of nodes shared between the processors
25199 const unsigned n_shd_nodes_iproc_jproc =
25200 local_node_alias[iproc][jproc].size();
25201#ifdef PARANOID
25202 // Read the number of nodes shared in the other direction
25203 const unsigned n_shd_nodes_jproc_iproc =
25204 local_node_alias[jproc][iproc].size();
25205
25206 if (n_shd_nodes_iproc_jproc != n_shd_nodes_jproc_iproc)
25207 {
25208 std::ostringstream error_stream;
25209 error_stream
25210 << "The number of nodes shared between iproc and jproc is\n"
25211 << "different from the number of nodes shared between jproc\n"
25212 << "and iproc\n"
25213 << "Nodes shared between processor (" << iproc << ") and "
25214 << "processor (" << jproc << "): (" << n_shd_nodes_iproc_jproc
25215 << ")\n"
25216 << "Nodes shared between processor (" << jproc << ") and "
25217 << "processor (" << iproc << "): (" << n_shd_nodes_jproc_iproc
25218 << ")\n\n";
25219 throw OomphLibError(
25220 error_stream.str(),
25221 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25222 OOMPH_EXCEPTION_LOCATION);
25223 } // if (n_shd_nodes_iproc_jproc != n_shd_nodes_jproc_iproc)
25224#endif
25225
25226 // Loop over the nodes shared between the processors
25227 for (unsigned ishd = 0; ishd < n_shd_nodes_iproc_jproc; ishd++)
25228 {
25229 // Get the number of alias associated to the node on each
25230 // processor
25231 const unsigned n_alias_iproc_jproc =
25232 local_node_alias[iproc][jproc][ishd].size();
25233 const unsigned n_alias_jproc_iproc =
25234 local_node_alias[jproc][iproc][ishd].size();
25235
25236 // Store all the found alias to the node
25237 Vector<Vector<unsigned>> node_alias;
25238
25239 // Flag to indicate if a new alias has been added
25240 bool new_alias_added = false;
25241
25242 // Start by adding the "direct" alias of the node
25243 for (unsigned ialias = 0; ialias < n_alias_iproc_jproc; ialias++)
25244 {
25245 // Get the alias of the node
25246 Vector<unsigned> current_alias =
25247 local_node_alias[iproc][jproc][ishd][ialias];
25248 // Check if already done
25249 if (!alias_done[current_alias])
25250 {
25251 // Add the alias of the node
25252 node_alias.push_back(current_alias);
25253 // Set the flag to indicate a new alias has been added
25254 new_alias_added = true;
25255 // Mark the alias as done
25256 alias_done[current_alias] = true;
25257 } // if (!alias_done[i_alias])
25258
25259 } // for (ialias < n_alias_iproc_jproc)
25260
25261 // Start by adding the "direct" alias of the node
25262 for (unsigned ialias = 0; ialias < n_alias_jproc_iproc; ialias++)
25263 {
25264 // Get the alias of the node
25265 Vector<unsigned> current_alias =
25266 local_node_alias[jproc][iproc][ishd][ialias];
25267
25268 // Check if already done
25269 if (!alias_done[current_alias])
25270 {
25271 // Add the alias of the node
25272 node_alias.push_back(current_alias);
25273 // Set the flag to indicate a new alias has been added
25274 new_alias_added = true;
25275 // Mark the alias as done
25276 alias_done[current_alias] = true;
25277 } // if (!alias_done[i_alias])
25278
25279 } // for (ialias < n_alias_jproc_iproc)
25280
25281 unsigned counter_alias = 0;
25282 // Visit the alias of the node and add any new found
25283 // alias, end until all its alias have been included
25284
25285 unsigned n_current_alias = node_alias.size();
25286 while (new_alias_added || counter_alias < n_current_alias)
25287 // while(new_alias_added) // we need to check all the alias,
25288 // including those added during the process
25289 {
25290 new_alias_added = false;
25291 // Store the current visited alias
25292 Vector<unsigned> current_alias = node_alias[counter_alias];
25293
25294 // Get the alias associated with the current alias
25295 Vector<Vector<unsigned>> alias_of_current_alias =
25296 local_node_alias[current_alias[0]][current_alias[1]]
25297 [current_alias[2]];
25298
25299 // Get all the alias associated with the alias of the
25300 // current alias
25301 const unsigned n_alias = alias_of_current_alias.size();
25302
25303 // Loop over the new alias and check if require to add
25304 // them
25305 for (unsigned k = 0; k < n_alias; k++)
25306 {
25307 // Get the alias of the node
25308 Vector<unsigned> add_alias = alias_of_current_alias[k];
25309
25310 // Check if already done
25311 if (!alias_done[add_alias])
25312 {
25313 // Add the alias of the node
25314 node_alias.push_back(add_alias);
25315 // Set the flag to indicate a new alias has been
25316 // added
25317 new_alias_added = true;
25318 // Mark the alias ad done
25319 alias_done[add_alias] = true;
25320 } // if (!alias_done[i_alias])
25321
25322 } // for (k < n_alias)
25323
25324 // Get the alias associated with the current alias (in the
25325 // other direction)
25326 Vector<Vector<unsigned>> alias_of_current_alias2 =
25327 local_node_alias[current_alias[1]][current_alias[0]]
25328 [current_alias[2]];
25329
25330 // Get all the alias associated with the current alias
25331 // (in the other direction)
25332 const unsigned n_alias2 = alias_of_current_alias2.size();
25333
25334 // Loop over the new alias and check if require to add
25335 // them
25336 for (unsigned k = 0; k < n_alias2; k++)
25337 {
25338 // Get the alias of the node
25339 Vector<unsigned> add_alias = alias_of_current_alias2[k];
25340
25341 // Check if already done
25342 if (!alias_done[add_alias])
25343 {
25344 // Add the alias of the node
25345 node_alias.push_back(add_alias);
25346 // Set the flag to indicate a new alias has been
25347 // added
25348 new_alias_added = true;
25349 // Mark the alias ad done
25350 alias_done[add_alias] = true;
25351 } // if (!alias_done[i_alias])
25352
25353 } // for (k < n_alias)
25354
25355 // Go for the next alias
25356 counter_alias++;
25357
25358 // Update the number of alias so that the while stops when
25359 // all the alias have been visited and no new alias was
25360 // added
25361 n_current_alias = node_alias.size();
25362
25363 } // while(new_alias_added || counter_alias < n_current_alias)
25364
25365 // If the node has not been previously added, then include
25366 // all its alias
25367 if (node_alias.size() > 0)
25368 {
25369 // Add all the found alias of the node to the global alias
25370 // storage
25371 global_node_alias.push_back(node_alias);
25372 }
25373
25374 } // for (ishd < n_shd_nodes_iproc_jproc)
25375
25376 } // for (jproc < nproc)
25377
25378 } // for (iproc < nproc)
25379
25380 // We now have the global number of nodes, each with its own id
25381 // (the index in the global_node_alias vector)
25382
25383 // Get the number of global shared nodes
25384 const unsigned n_global_shared_nodes = global_node_alias.size();
25385
25386 // Create matrix from local to global shared node id
25387 Vector<Vector<Vector<int>>> local_to_global_shared_node(nproc);
25388
25389 // Loop over all the processors to resize
25390 for (unsigned iproc = 0; iproc < nproc; iproc++)
25391 {
25392 // Resize the map matrix
25393 local_to_global_shared_node[iproc].resize(nproc);
25394 } // for (iproc < nproc)
25395
25396 // Loop over all the processors to resize (the third direction,
25397 // required if we want to loop over the half of the matrix only)
25398 for (unsigned iproc = 0; iproc < nproc; iproc++)
25399 {
25400 // Loop over the half of the matrix to resize
25401 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25402 {
25403 // Read the number of nodes shared between the processors
25404 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25405
25406 // Resize the map matrix
25407 local_to_global_shared_node[iproc][jproc].resize(n_shd_nodes, -1);
25408
25409 // ... and resize the other half map matrix
25410 local_to_global_shared_node[jproc][iproc].resize(n_shd_nodes, -1);
25411
25412 } // for (jproc < nproc)
25413
25414 } // for (iproc < nproc)
25415
25416 // Fill the matrix for mapping from local to global node id
25417
25418 // Loop over the global nodes, and for each alias assign the
25419 // corresponding global node id
25420 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25421 {
25422 // Get the number of alias associated to the current global node
25423 const unsigned n_alias_global_node = global_node_alias[k].size();
25424 // Loop over the alias and assign the global node id
25425 for (unsigned l = 0; l < n_alias_global_node; l++)
25426 {
25427 // Get the 1st processor
25428 const unsigned iproc = global_node_alias[k][l][0];
25429 // Get the 2nd processor
25430 const unsigned jproc = global_node_alias[k][l][1];
25431 // Get the node number
25432 const unsigned ishd = global_node_alias[k][l][2];
25433 // Assign the global node id
25434 local_to_global_shared_node[iproc][jproc][ishd] = k;
25435
25436 } // for (l < n_alias_global_node)
25437
25438 } // for (k < n_global_shared_nodes)
25439
25440 // Create the global adjacency matrix
25441 Vector<Vector<unsigned>> global_adjacency_matrix(n_global_shared_nodes);
25442 // Resize the global adjacency matrix
25443 for (unsigned k = 0; k < n_global_shared_nodes; k++)
25444 {
25445 // Resize
25446 global_adjacency_matrix[k].resize(n_global_shared_nodes, 0);
25447 } // for (k < n_global_shared_nodes)
25448
25449 // Add the entries to the global adjacency matrix and compute the
25450 // degree of each node
25451
25452 // Store the degree of the global nodes
25453 Vector<unsigned> global_node_degree(n_global_shared_nodes, 0);
25454
25455 // Loop over the processors
25456 for (unsigned iproc = 0; iproc < nproc; iproc++)
25457 {
25458 // Loop over the half of the matrix to resize
25459 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25460 {
25461 // Get the number of nodes shared between the processors
25462 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25463
25464 // Search for entries in the local adjacency matrix that set a
25465 // connection among the nodes
25466
25467 // Loop over the shared nodes in the current pair of
25468 // processors
25469 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25470 {
25471 for (unsigned jshd = ishd + 1; jshd < n_shd_nodes; jshd++)
25472 {
25473 // Are the nodes associated
25474 if (local_adjacency_matrix[iproc][jproc][ishd][jshd] > 0)
25475 {
25476 // Get the global nodes id
25477
25478 // Get the "left-node" global id
25479 const int global_shd_node_left =
25480 local_to_global_shared_node[iproc][jproc][ishd];
25481
25482 // Get the "right-node" global id
25483 const int global_shd_node_right =
25484 local_to_global_shared_node[iproc][jproc][jshd];
25485
25486#ifdef PARANOID
25487 // Check if the local nodes have a global node
25488 // associated
25489 if (global_shd_node_left == -1)
25490 {
25491 std::ostringstream error_stream;
25492 error_stream
25493 << "The local node in processors iproc and jproc has no\n"
25494 << "global node assigned\n"
25495 << "iproc processor: (" << iproc << ")\n"
25496 << "jproc processor: (" << jproc << ")\n"
25497 << "Local node: (" << ishd << ")\n\n";
25498 throw OomphLibError(error_stream.str(),
25499 "RefineableTriangleMesh::compute_shared_"
25500 "node_degree_helper()",
25501 OOMPH_EXCEPTION_LOCATION);
25502 }
25503
25504 // Check if the local nodes have a global node
25505 // associated
25506 if (global_shd_node_right == -1)
25507 {
25508 std::ostringstream error_stream;
25509 error_stream
25510 << "The local node in processors iproc and jproc has no\n"
25511 << "global node assigned\n"
25512 << "iproc processor: (" << iproc << ")\n"
25513 << "jproc processor: (" << jproc << ")\n"
25514 << "Local node: (" << jshd << ")\n\n";
25515 throw OomphLibError(error_stream.str(),
25516 "RefineableTriangleMesh::compute_shared_"
25517 "node_degree_helper()",
25518 OOMPH_EXCEPTION_LOCATION);
25519 }
25520#endif
25521 // Get the unsigned version of the indexes
25522 const unsigned uleft =
25523 static_cast<unsigned>(global_shd_node_left);
25524 const unsigned uright =
25525 static_cast<unsigned>(global_shd_node_right);
25526
25527 // Add the entry in the global adjacency matrix
25528 global_adjacency_matrix[uleft][uright]++;
25529
25530 // ... and in the other direction too
25531 global_adjacency_matrix[uright][uleft]++;
25532
25533 // Add on to the degree of the left node
25534 global_node_degree[uleft]++;
25535
25536 // Add on to the degree of the right node
25537 global_node_degree[uright]++;
25538
25539 } // if (local_adjacency_matrix[iproc][jproc][ishd][jshd] > 0)
25540
25541 } // // for (jshd < n_shd_nodes)
25542
25543 } // for (ishd < n_shd_nodes)
25544
25545 } // for (jproc < nproc)
25546
25547 } // for (iproc < nproc)
25548
25549 // Assign the global degree to the shared nodes between each pair
25550 // of processors
25551 Vector<Vector<Vector<unsigned>>> root_local_node_degree(nproc);
25552 // Resize the container
25553 for (unsigned iproc = 0; iproc < nproc; iproc++)
25554 {
25555 root_local_node_degree[iproc].resize(nproc);
25556 }
25557
25558 // Loop over the processors and visited their shared nodes
25559 for (unsigned iproc = 0; iproc < nproc; iproc++)
25560 {
25561 // Only visit the half of the data
25562 for (unsigned jproc = iproc + 1; jproc < nproc; jproc++)
25563 {
25564 // Get the number of shared nodes between this pair of
25565 // processors (iproc, jproc)
25566 const unsigned n_shd_nodes = local_node_alias[iproc][jproc].size();
25567
25568 // Resize the container to store the local degree of the nodes
25569 root_local_node_degree[iproc][jproc].resize(n_shd_nodes);
25570 // ... and in the other way too
25571 root_local_node_degree[jproc][iproc].resize(n_shd_nodes);
25572
25573 // Loop over the number of nodes shared between the pair of
25574 // processors
25575 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25576 {
25577 // Get the global node id for the current shared node
25578 const int global_shd_node_id =
25579 local_to_global_shared_node[iproc][jproc][ishd];
25580
25581#ifdef PARANOID
25582 // Check if the local nodes have a global node associated
25583 if (global_shd_node_id == -1)
25584 {
25585 std::ostringstream error_stream;
25586 error_stream
25587 << "The local node in processors iproc and jproc has no\n"
25588 << "global node assigned\n"
25589 << "iproc processor: (" << iproc << ")\n"
25590 << "jproc processor: (" << jproc << ")\n"
25591 << "Local node: (" << ishd << ")\n\n";
25592 throw OomphLibError(
25593 error_stream.str(),
25594 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25595 OOMPH_EXCEPTION_LOCATION);
25596 }
25597#endif
25598
25599 // Get the unsigned version of the global index
25600 const unsigned uglobal_shd_node_id =
25601 static_cast<unsigned>(global_shd_node_id);
25602
25603 // Get the degree of the node
25604 const unsigned node_degree =
25605 global_node_degree[uglobal_shd_node_id];
25606
25607 // Set the degree in the container for the degree of the
25608 // nodes in the local interaction between processors
25609 root_local_node_degree[iproc][jproc][ishd] = node_degree;
25610 // ... and in the other way too
25611 root_local_node_degree[jproc][iproc][ishd] = node_degree;
25612
25613 } // for (ishd < n_shd_nodes)
25614
25615 } // for (jproc < nproc)
25616
25617 } // for (iproc < nproc)
25618
25619 // Clear the container where the info. will be sent back to each
25620 // processor
25621 package_unsigned_data_sent_from_root.clear();
25622
25623 // Prepare the data to sent it back to each processor (encode the
25624 // info. to sent to all processors)
25625 for (unsigned iproc = 0; iproc < nproc; iproc++)
25626 {
25627 // Count the number of data sent to iproc processor
25628 unsigned count_n_data_sent_to_iproc = 0;
25629 for (unsigned jproc = 0; jproc < nproc; jproc++)
25630 {
25631 // No shared nodes between the same processor
25632 if (iproc != jproc)
25633 {
25634 // Get the number of nodes shared between the processors
25635 const unsigned n_shd_nodes =
25636 root_local_node_degree[iproc][jproc].size();
25637
25638 // Add the number of data sent to iproc processor
25639 count_n_data_sent_to_iproc += n_shd_nodes;
25640
25641 // Loop over the nodes shared between the pair of processors
25642 for (unsigned ishd = 0; ishd < n_shd_nodes; ishd++)
25643 {
25644 package_unsigned_data_sent_from_root.push_back(
25645 root_local_node_degree[iproc][jproc][ishd]);
25646 } // for (ishd < n_shd_nodes)
25647
25648 } // if (iproc != jproc)
25649
25650 } // for (jproc < nproc)
25651
25652 // Set the number of data sent to iproc processor
25653 n_unsigned_data_sent_from_root[iproc] = count_n_data_sent_to_iproc;
25654
25655 } // for (iproc < nproc)
25656
25657 } // if (my_rank == root_processor)
25658
25659 // Total data received from root to this processor
25660 int n_unsigned_data_received_from_root = 0;
25661
25662 // Get the number of data that each processor receives from root
25663 MPI_Scatter(&n_unsigned_data_sent_from_root[0], // Info. sent from
25664 // root to each
25665 // processor
25666 1, // The number of data sent from root to each
25667 // processor
25668 MPI_UNSIGNED,
25669 &n_unsigned_data_received_from_root, // Store the
25670 // info. received
25671 // from root
25672 1, // The number of data received from root
25673 MPI_UNSIGNED,
25674 root_processor, // The processor that sends the
25675 // info.
25676 comm_pt->mpi_comm());
25677
25678 // Receive the info. sent by root
25679 Vector<unsigned> package_unsigned_data_received_from_root(
25680 n_unsigned_data_received_from_root);
25681
25682 // Compute the offsets to each processor
25683 Vector<int> root_unsigned_offsets_sent(nproc, 0);
25684 root_unsigned_offsets_sent[0] = 0;
25685 for (unsigned iproc = 1; iproc < nproc; iproc++)
25686 {
25687 // Compute the offset to send the values to each processor
25688 root_unsigned_offsets_sent[iproc] =
25689 root_unsigned_offsets_sent[iproc - 1] +
25690 n_unsigned_data_sent_from_root[iproc - 1];
25691 }
25692
25693 if (my_rank != root_processor)
25694 {
25695 // Create at least one entry so we don't get a seg fault below
25696 if (package_unsigned_data_sent_from_root.size() == 0)
25697 {
25698 package_unsigned_data_sent_from_root.resize(1);
25699 }
25700 } // if (my_rank!=root_processor)
25701
25702 // Create at least one entry so we don't get a seg fault below
25703 if (package_unsigned_data_received_from_root.size() == 0)
25704 {
25705 package_unsigned_data_received_from_root.resize(1);
25706 }
25707
25708 // Get the data from root
25709 MPI_Scatterv(&package_unsigned_data_sent_from_root[0], // The
25710 // info. sent
25711 // from root
25712 // to others
25713 // processors
25714 &n_unsigned_data_sent_from_root[0], // The number of
25715 // data sent from
25716 // root to others
25717 // processors
25718 &root_unsigned_offsets_sent[0], // The offsets to each
25719 // processors
25720 MPI_UNSIGNED,
25721 &package_unsigned_data_received_from_root[0], // The
25722 // storage
25723 // in the
25724 // processor
25725 // that
25726 // receives
25727 // the
25728 // info.
25729 n_unsigned_data_received_from_root, // The number of
25730 // data that the
25731 // current
25732 // processor
25733 // receives from
25734 // root
25735 MPI_UNSIGNED,
25736 root_processor, // The root processors
25737 comm_pt->mpi_comm());
25738
25739 // Decode the info.
25740
25741 // Keep track of the already nodes done
25742 std::map<Node*, bool> node_done;
25743
25744 // Read the global degree assigned to the shared nodes between the
25745 // current processors and the other processors
25746 int decode_counter = 0;
25747 // Store the global degree of the local nodes
25748 Vector<Vector<unsigned>> local_node_degree(nproc);
25749 // Loop over the processors
25750 for (unsigned iproc = 0; iproc < nproc; iproc++)
25751 {
25752 // There are no shared nodes with the current processor itself
25753 if (iproc != my_rank)
25754 {
25755 // Get the number of nodes shared with the iproc processor
25756 const unsigned n_nodes = tmp_sorted_shared_node_pt[iproc].size();
25757
25758 // Read the global degree of the node
25759 package_unsigned_send_data_to_root.push_back(n_nodes);
25760
25761 // Loop over the nodes
25762 for (unsigned ishd = 0; ishd < n_nodes; ishd++)
25763 {
25764 // Get the node degree assigned to the ishd node in between
25765 // the interaction of the iproc and the current processor
25766 const unsigned node_degree =
25767 package_unsigned_data_received_from_root[decode_counter++];
25768
25769 // Get the node
25770 Node* shd_node_pt = tmp_sorted_shared_node_pt[iproc][ishd];
25771
25772 // Has the node been assigned a global degree
25773 if (!node_done[shd_node_pt])
25774 {
25775 // Assign the global degree to the node
25776 global_node_degree[shd_node_pt] = node_degree;
25777 // Mark the node as done
25778 node_done[shd_node_pt] = true;
25779 }
25780#ifdef PARANOID
25781 else
25782 {
25783 // The node has been already done, check that the node
25784 // degree is the same as the already assigned
25785 if (global_node_degree[shd_node_pt] != node_degree)
25786 {
25787 std::ostringstream error_stream;
25788 error_stream
25789 << "The local node has already assigned a global degree,\n"
25790 << "however, a different degree for the same node has been\n"
25791 << "read from the data sent from root processor\n"
25792 << "iproc processor: (" << iproc << ")\n"
25793 << "Local node: (" << ishd << ")\n"
25794 << "---------------------------------------------------------\n"
25795 << "Already assigned degree: ("
25796 << global_node_degree[shd_node_pt] << ")\n"
25797 << "New found degree: (" << node_degree << ")\n"
25798 << "---------------------------------------------------------\n"
25799 << "Node coordinates: (" << shd_node_pt->x(0) << ", "
25800 << shd_node_pt->x(1) << ")\n\n";
25801 throw OomphLibError(
25802 error_stream.str(),
25803 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25804 OOMPH_EXCEPTION_LOCATION);
25805 }
25806
25807 } // else if (!node_done[shd_node_pt])
25808#endif // #ifdef PARANOID
25809
25810 } // for (ishd < n_nodes)
25811
25812 } // if (iproc != my_rank)
25813
25814 } // for (iproc < nproc)
25815
25816#ifdef PARANOID
25817 // Ensure that all the info. sent from root processor has been read
25818 if (decode_counter != n_unsigned_data_received_from_root)
25819 {
25820 std::ostringstream error_stream;
25821 error_stream
25822 << "The number of data decoded received from root processor is\n"
25823 << "different from the total number of data received from the root\n"
25824 << "processor\n"
25825 << "Data decoded: (" << decode_counter << ")\n"
25826 << "Data received: (" << n_unsigned_data_received_from_root << ")\n\n"
25827 << "This is a synchronisation issue so you are probably sending\n"
25828 << "more or less info. than the one that is being decoded\n\n";
25829 throw OomphLibError(
25830 error_stream.str(),
25831 "RefineableTriangleMesh::compute_shared_node_degree_helper()",
25832 OOMPH_EXCEPTION_LOCATION);
25833 }
25834#endif
25835 }
25836
25837 //======================================================================
25838 // Sort the nodes on the new shared boundaries (after load balancing),
25839 // computes the alias of the nodes and creates the adjacency matrix
25840 // that represent the graph created by the shared edges between each
25841 // pair of processors
25842 // ======================================================================
25843 template<class ELEMENT>
25846 Vector<Vector<FiniteElement*>>& unsorted_face_ele_pt,
25847 Vector<Vector<Node*>>& tmp_sorted_shared_node_pt,
25848 std::map<Node*, Vector<Vector<unsigned>>>& node_alias,
25849 Vector<Vector<Vector<unsigned>>>& adjacency_matrix)
25850 {
25851 // Get the number of processors and the rank
25852 const unsigned nproc = this->communicator_pt()->nproc();
25853 const unsigned my_rank = this->communicator_pt()->my_rank();
25854
25855 // Assign a unique id to each node shared between each pair of
25856 // processors, in this case the current processor and the iproc
25857
25858 // ... also compute the alias of each node (processor and index of
25859 // the node in all processors where it appears)
25860
25861 // Clear the alias info
25862 node_alias.clear();
25863
25864 // Temporary storage for the index of the nodes
25865 Vector<std::map<Node*, unsigned>> tmp_node_index(nproc);
25866
25867 // Loop over the processors
25868 for (unsigned iproc = 0; iproc < nproc; iproc++)
25869 {
25870 // There is no shared elements between the same processor
25871 if (iproc != my_rank)
25872 {
25873 // Map to mark those nodes already visited
25874 std::map<Node*, bool> done_node;
25875
25876 // A map is used to sort the nodes using their coordinates as
25877 // the key of the map
25878 // std::map<std::pair<double, double>, Node*> sorted_nodes_pt;
25879 std::map<std::pair<double, double>, Node*, classcomp> sorted_nodes_pt;
25880
25881 // Get the number of unsorted face elements
25882 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
25883
25884 // Loop over the unsorted elements
25885 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25886 {
25887 // Get a root element
25888 FiniteElement* face_ele_pt = unsorted_face_ele_pt[iproc][e];
25889 // Get the left node of the face element
25890 Node* left_node_pt = face_ele_pt->node_pt(0);
25891
25892 // Check if the node has been already sorted in the
25893 // interaction between the current processor and iproc
25894 // processor
25895 if (!done_node[left_node_pt])
25896 {
25897 std::pair<double, double> vertex =
25898 std::make_pair(left_node_pt->x(0), left_node_pt->x(1));
25899 sorted_nodes_pt[vertex] = left_node_pt;
25900 // Mark the node as done
25901 done_node[left_node_pt] = true;
25902 }
25903
25904 // Get the number of nodes of the face element
25905 const unsigned n_nodes = face_ele_pt->nnode();
25906 // Get the right node of the face element
25907 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
25908
25909 // Check if the node has been already sorted in the
25910 // interaction between the current processor and iproc
25911 // processor
25912 if (!done_node[right_node_pt])
25913 {
25914 std::pair<double, double> vertex =
25915 std::make_pair(right_node_pt->x(0), right_node_pt->x(1));
25916 sorted_nodes_pt[vertex] = right_node_pt;
25917 // Mark the node as done
25918 done_node[right_node_pt] = true;
25919 }
25920
25921 } // for (e < nunsorted_face_ele)
25922
25923 // The nodes are already sorted, we need to return them in the
25924 // proper container
25925
25926 // The counter to enumerate the nodes
25927 unsigned counter = 0;
25928
25929 // Go through the map container which already have the nodes
25930 // sorted they have the same sorting on all processors
25931 for (std::map<std::pair<double, double>, Node*>::iterator it =
25932 sorted_nodes_pt.begin();
25933 it != sorted_nodes_pt.end();
25934 it++)
25935 {
25936 // Get the node
25937 Node* node_pt = (*it).second;
25938 // Store the node at the corresponding index
25939 tmp_sorted_shared_node_pt[iproc].push_back(node_pt);
25940
25941 // Create the temporary access to the node index
25942 tmp_node_index[iproc][node_pt] = counter;
25943
25944 // Fill the info. for the node alias
25945 Vector<unsigned> alias(3);
25946 // The current processor
25947 alias[0] = my_rank;
25948 // The processor with which is shared
25949 alias[1] = iproc;
25950 // The index with that processor
25951 alias[2] = counter++;
25952
25953 // Store the info. of the alias
25954 node_alias[node_pt].push_back(alias);
25955
25956 } // Loop map
25957
25958 } // if (iproc != my_rank)
25959
25960 } // for (iproc < nproc)
25961
25962 // Loop over the processors to resize and initialize the adjacency
25963 // matrix
25964 for (unsigned iproc = 0; iproc < nproc; iproc++)
25965 {
25966 // Get the number of nodes shared with iproc
25967 const unsigned n_shd_nodes = tmp_sorted_shared_node_pt[iproc].size();
25968 // Resize the adjacency matrix
25969 adjacency_matrix[iproc].resize(n_shd_nodes);
25970 for (unsigned i = 0; i < n_shd_nodes; i++)
25971 {
25972 // Resize the adjacency matrix
25973 adjacency_matrix[iproc][i].resize(n_shd_nodes);
25974
25975 // Initialize the
25976 for (unsigned j = 0; j < n_shd_nodes; j++)
25977 {
25978 adjacency_matrix[iproc][i][j] = 0;
25979 } // for (j < n_shd_nodes)
25980
25981 } // for (i < n_shd_nodes)
25982
25983 } // for (iproc < nproc)
25984
25985 // Loop over the processors to fill the adjacency matrix
25986 for (unsigned iproc = 0; iproc < nproc; iproc++)
25987 {
25988 // There is no shared elements between the same processor
25989 if (iproc != my_rank)
25990 {
25991 // Get the number of unsorted face elements
25992 const unsigned n_unsorted_face_ele = unsorted_face_ele_pt[iproc].size();
25993
25994 // Loop over the unsorted elements
25995 for (unsigned e = 0; e < n_unsorted_face_ele; e++)
25996 {
25997 // Get a root element
25998 FiniteElement* face_ele_pt = unsorted_face_ele_pt[iproc][e];
25999 // Get the left node of the face element
26000 Node* left_node_pt = face_ele_pt->node_pt(0);
26001
26002 // Get the number of nodes of the face element
26003 const unsigned n_nodes = face_ele_pt->nnode();
26004 // Get the right node of the face element
26005 Node* right_node_pt = face_ele_pt->node_pt(n_nodes - 1);
26006
26007 // Get the index of each of the nodes
26008 const unsigned left_node_index = tmp_node_index[iproc][left_node_pt];
26009 const unsigned right_node_index =
26010 tmp_node_index[iproc][right_node_pt];
26011
26012 // Add an entry to the adjacency matrix to indicate the
26013 // association of left and right node
26014 adjacency_matrix[iproc][left_node_index][right_node_index]++;
26015 // ... both directions
26016 adjacency_matrix[iproc][right_node_index][left_node_index]++;
26017
26018 } // for (e < n_unsorted_face_ele)
26019
26020 } // if (iproc != my_rank)
26021
26022 } // for (iproc < nproc)
26023 }
26024
26025 //======================================================================
26026 /// Get the nodes on the shared boundary (b), these are stored
26027 /// in the segment they belong
26028 //======================================================================
26029 template<class ELEMENT>
26032 const unsigned& shd_bnd_id, Vector<Vector<Node*>>& tmp_segment_nodes)
26033 {
26034 // Clear the data structure were to return the nodes
26035 tmp_segment_nodes.clear();
26036
26037 // Get the face elements that created the shared boundary from the
26038 // bulk shared boundary elements
26039
26040#ifdef PARANOID
26041 // The temporary storage for the halo face elements
26042 Vector<FiniteElement*> halo_shared_face_ele_pt;
26043#endif
26044 // The temporary storage for the nonhalo face elements
26045 Vector<FiniteElement*> nonhalo_shared_face_ele_pt;
26046
26047 // Get the number of shared boundary elements associated with the
26048 // current shared boundary
26049 const unsigned nshared_bound_ele =
26050 this->nshared_boundary_element(shd_bnd_id);
26051
26052 // Loop over the elements in the shared boundary to create the face
26053 // elements
26054 for (unsigned e = 0; e < nshared_bound_ele; e++)
26055 {
26056 // Get the shared boundary element
26057 FiniteElement* bulk_ele_pt =
26058 this->shared_boundary_element_pt(shd_bnd_id, e);
26059
26060 // Get the face index
26061 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
26062
26063 // Before adding the new element we need to ensure that the edge
26064 // that this element represents has not been already added
26065 FiniteElement* face_ele_pt =
26066 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
26067
26068 // Nonhalo element
26069 if (!bulk_ele_pt->is_halo())
26070 {
26071 // Add nonhalo shared face element to the container
26072 nonhalo_shared_face_ele_pt.push_back(face_ele_pt);
26073 }
26074#ifdef PARANOID
26075 else // halo element
26076 {
26077 // Add halo shared face element to the container
26078 halo_shared_face_ele_pt.push_back(face_ele_pt);
26079 }
26080#endif
26081
26082 } // for (e < nshared_bound_ele)
26083
26084 // Mark the face elements already used
26085 std::map<FiniteElement*, bool> shared_face_done;
26086
26087 // Get the number of nonhalo face elements
26088 const unsigned nnonhalo_face_shared_ele = nonhalo_shared_face_ele_pt.size();
26089
26090 // If we are in PARANOID mode check that there is one halo element
26091 // for each nonhalo element
26092#ifdef PARANOID
26093 // Get the number of halo face elements
26094 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
26095
26096 // The number of nonhalo shared face boundary elements must be the
26097 // half of the total number of shared boundary elements
26098 if (nshared_bound_ele / 2 != nnonhalo_face_shared_ele)
26099 {
26100 std::ostringstream error_message;
26101 error_message
26102 << "The number of shared boundary elements (" << nshared_bound_ele
26103 << ") is not the double\nof the number of unsorted nonhalo shared "
26104 << "face boundary elements (" << nnonhalo_face_shared_ele
26105 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26106 throw OomphLibError(
26107 error_message.str(),
26108 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26109 OOMPH_EXCEPTION_LOCATION);
26110 }
26111
26112 // The number of halo shared face boundary elements must be the
26113 // half of the total number of shared boundary elements
26114 if (nshared_bound_ele / 2 != nhalo_face_shared_ele)
26115 {
26116 std::ostringstream error_message;
26117 error_message
26118 << "The number of shared boundary elements (" << nshared_bound_ele
26119 << ") is not the double\nof the number of unsorted halo shared "
26120 << "face boundary elements (" << nhalo_face_shared_ele
26121 << ")\n for the current boundary (" << shd_bnd_id << ")\n\n";
26122 throw OomphLibError(
26123 error_message.str(),
26124 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26125 OOMPH_EXCEPTION_LOCATION);
26126 }
26127
26128 // ------------------------------------------------------------------
26129 // Loop over the nonhalo face elements and look for the halo face
26130 // element at the other side of the shared boundary
26131 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26132 {
26133 // Get the inh-th face element
26134 FiniteElement* nonhalo_face_ele_pt = nonhalo_shared_face_ele_pt[inh];
26135
26136 // Get the number of nodes on the face element
26137 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
26138 // Get the first and last node on the element
26139 Node* nh_first_node_pt = nonhalo_face_ele_pt->node_pt(0);
26140 Node* nh_last_node_pt = nonhalo_face_ele_pt->node_pt(nnodes_nh - 1);
26141
26142 // Now find the (halo) face element at the other side of the
26143 // shared boundary
26144 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26145 {
26146 // Get the ih-th face element
26147 FiniteElement* halo_face_ele_pt = halo_shared_face_ele_pt[ih];
26148
26149 // Check that the face element has not been done
26150 if (!shared_face_done[halo_face_ele_pt])
26151 {
26152 // Get the number of nodes on the face element
26153 const unsigned nnodes_h = halo_face_ele_pt->nnode();
26154 // Get the first and last node on the element
26155 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
26156 Node* h_last_node_pt = halo_face_ele_pt->node_pt(nnodes_h - 1);
26157
26158 // If the nodes are the same then we have found the (halo)
26159 // face element at the other side of the shared boundary
26160 if (nh_first_node_pt == h_first_node_pt &&
26161 nh_last_node_pt == h_last_node_pt)
26162 {
26163 // Mark the face elements as done
26164 shared_face_done[nonhalo_face_ele_pt] = true;
26165 shared_face_done[halo_face_ele_pt] = true;
26166
26167 // Break the loop for (ih < nhalo_face_shared_ele)
26168 break;
26169 } // if (nh_first_node_pt == h_first_node_pt &&
26170 // nh_last_node_pt == h_last_node_pt)
26171 else if (nh_first_node_pt == h_last_node_pt &&
26172 nh_last_node_pt == h_first_node_pt)
26173 {
26174 // Mark the face elements as done
26175 shared_face_done[nonhalo_face_ele_pt] = true;
26176 shared_face_done[halo_face_ele_pt] = true;
26177
26178 // Break the loop for (ih < nhalo_face_shared_ele)
26179 break;
26180 } // else if (nh_first_node_pt == h_last_node_pt &&
26181 // nh_last_node_pt == h_first_node_pt)
26182
26183 } // if (face_done[halo_face_ele_pt])
26184
26185 } // for (ih < nhalo_face_shared_ele)
26186
26187 } // for (inh < nnonhalo_face_shared_ele)
26188
26189 // The number of done shared face elements MUST be the same as the
26190 // sum of the nonhalo and halo shared boundary face elements
26191 if ((nnonhalo_face_shared_ele + nhalo_face_shared_ele) !=
26192 shared_face_done.size())
26193 {
26194 std::ostringstream error_message;
26195 error_message << "The number of DONE shared boundary face elements ("
26196 << shared_face_done.size()
26197 << ") is not the same\n as the sum of"
26198 << "the nonhalo face shared boundary elements ("
26199 << nnonhalo_face_shared_ele
26200 << ")\nand the halo face shared "
26201 << "boundary elements (" << nhalo_face_shared_ele
26202 << ") for the\n/"
26203 << "current boundary (" << shd_bnd_id << ")\n\n";
26204 throw OomphLibError(
26205 error_message.str(),
26206 "RefineableTriangleMesh::get_shared_boundary_segment_nodes_helper()",
26207 OOMPH_EXCEPTION_LOCATION);
26208 }
26209#endif // #ifdef PARANOID
26210
26211 // -------------------------------------------------------------
26212 // Now sort the face elements
26213 // -------------------------------------------------------------
26214
26215 // We already have the shared face elements that make the shared
26216 // boundary now sort them to create a contiguous boundary
26217
26218 // Clear the already done face elements
26219 shared_face_done.clear();
26220
26221 unsigned nsorted_face_ele = 0;
26222
26223 // Storing for the sorting nodes extracted from the face elements
26224 std::list<Node*> sorted_nodes;
26225
26226 // Get the root face element
26227 FiniteElement* root_face_ele_pt = nonhalo_shared_face_ele_pt[0];
26228 nsorted_face_ele++;
26229
26230 // Mark face as done
26231 shared_face_done[root_face_ele_pt] = true;
26232
26233 // The initial and final node on the list
26234 const unsigned nnodes_root = root_face_ele_pt->nnode();
26235 Node* first_node_pt = root_face_ele_pt->node_pt(0);
26236 Node* last_node_pt = root_face_ele_pt->node_pt(nnodes_root - 1);
26237
26238 // Push back on the list the new nodes
26239 sorted_nodes.push_back(first_node_pt);
26240 sorted_nodes.push_back(last_node_pt);
26241
26242 // Sort the face elements
26243 while (nsorted_face_ele < nnonhalo_face_shared_ele)
26244 {
26245 // Flag to indicate when a node was added
26246 bool node_added = false;
26247
26248 // Start from the next edge since we have already added the
26249 // previous one as the initial face element
26250 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
26251 {
26252 FiniteElement* tmp_shared_face_ele_pt =
26253 nonhalo_shared_face_ele_pt[iface];
26254
26255 // If face has not been sorted
26256 if (!shared_face_done[tmp_shared_face_ele_pt])
26257 {
26258 // Get the number of nodes for the current face element
26259 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
26260
26261 // Get each individual node
26262 Node* left_node_pt = tmp_shared_face_ele_pt->node_pt(0);
26263 Node* right_node_pt = tmp_shared_face_ele_pt->node_pt(tmp_nnodes - 1);
26264
26265 if (left_node_pt == first_node_pt)
26266 {
26267 // Push front the new node
26268 sorted_nodes.push_front(right_node_pt);
26269 first_node_pt = right_node_pt;
26270 node_added = true;
26271 }
26272 else if (left_node_pt == last_node_pt)
26273 {
26274 // Push back the new node
26275 sorted_nodes.push_back(right_node_pt);
26276 last_node_pt = right_node_pt;
26277 node_added = true;
26278 }
26279 else if (right_node_pt == first_node_pt)
26280 {
26281 // Push front the new node
26282 sorted_nodes.push_front(left_node_pt);
26283 first_node_pt = left_node_pt;
26284 node_added = true;
26285 }
26286 else if (right_node_pt == last_node_pt)
26287 {
26288 // Push back the new node
26289 sorted_nodes.push_back(left_node_pt);
26290 last_node_pt = left_node_pt;
26291 node_added = true;
26292 }
26293
26294 if (node_added)
26295 {
26296 // Mark as done only if one of its nodes has been added to
26297 // the list
26298 shared_face_done[tmp_shared_face_ele_pt] = true;
26299 nsorted_face_ele++;
26300
26301 // Break the for
26302 break;
26303 }
26304
26305 } // if (!shared_face_done[tmp_shared_face_ele_pt])
26306
26307 } // for (iface < nnonhalo_face_shared_ele)
26308
26309 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
26310
26311 // Here we can safely delete the face elements, they are no longer
26312 // required
26313
26314 // First the nonhalo face elements
26315 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
26316 {
26317 delete nonhalo_shared_face_ele_pt[inh];
26318 nonhalo_shared_face_ele_pt[inh] = 0;
26319 } // for (inh < nnonhalo_face_shared_ele)
26320
26321#ifdef PARANOID
26322 // ... then the halo face elements
26323 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
26324 {
26325 delete halo_shared_face_ele_pt[ih];
26326 halo_shared_face_ele_pt[ih] = 0;
26327 } // for (inh < nhalo_face_shared_ele)
26328#endif
26329
26330 // ------------------------------------------------
26331 // Now copy the nodes to the output container
26332 // ------------------------------------------------
26333 // Get the number of nodes in the container
26334 const unsigned n_nodes = sorted_nodes.size();
26335
26336 // First resize the container
26337 tmp_segment_nodes.resize(1);
26338 tmp_segment_nodes[0].resize(n_nodes);
26339
26340 // Counter
26341 unsigned counter = 0;
26342
26343 // Loop over the list of nodes and copy them in the output container
26344 for (std::list<Node*>::iterator it = sorted_nodes.begin();
26345 it != sorted_nodes.end();
26346 it++)
26347 {
26348 tmp_segment_nodes[0][counter] = (*it);
26349 counter++;
26350 } // Loop over sorted nodes
26351 }
26352
26353 //=====start of get_required_elemental_information_load_balance_helper====
26354 /// Helper function to get the required elemental information from
26355 /// the element that will be sent to iproc processor.
26356 /// This info. involves the association of the element to a boundary or
26357 /// region.
26358 //========================================================================
26359 template<class ELEMENT>
26362 unsigned& iproc,
26363 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
26364 FiniteElement* ele_pt)
26365 {
26366 // Check if the element is associated with the original boundaries
26367 const unsigned nbound = this->initial_shared_boundary_id();
26368
26369 // Get the number of processors
26370 const unsigned nproc = this->communicator_pt()->nproc();
26371
26372 // ------------------------------------------------------------------
26373 // Stores the information regarding the boundaries associated to the
26374 // element (it that is the case)
26375 Vector<unsigned> associated_boundaries;
26376 Vector<unsigned> face_index_on_boundary;
26377
26378 unsigned counter_face_indexes = 0;
26379
26380 for (unsigned b = 0; b < nbound; b++)
26381 {
26382 // Get the number of elements associated to boundary i
26383 const unsigned nboundary_ele = nboundary_element(b);
26384 for (unsigned e = 0; e < nboundary_ele; e++)
26385 {
26386 if (ele_pt == this->boundary_element_pt(b, e))
26387 {
26388 // Keep track of the boundaries associated to the element
26389 associated_boundaries.push_back(b);
26390 // Get the face index
26391 face_index_on_boundary.push_back(face_index_at_boundary(b, e));
26392 counter_face_indexes++;
26393#ifdef PARANOID
26394 if (counter_face_indexes > 2)
26395 {
26396 std::stringstream error_message;
26397 error_message
26398 << "A triangular element can not have more than two of its faces "
26399 << "on a boundary!!!\n\n";
26400 throw OomphLibError(error_message.str(),
26401 "RefineableTriangleMesh::get_required_"
26402 "elemental_information_helper()",
26403 OOMPH_EXCEPTION_LOCATION);
26404 }
26405#else
26406 // Already found 2 face indexes on the same boundary?
26407 if (counter_face_indexes == 2)
26408 {
26409 break;
26410 }
26411#endif // #ifdef PARANOID
26412
26413 } // if (ele_pt == this->boundary_element_pt(b,e))
26414
26415 } // (e < nboundary_ele)
26416
26417 } // (b < nbound)
26418
26419 // If the element is associated to any boundary then package all the
26420 // relevant info
26421 const unsigned nassociated_boundaries = associated_boundaries.size();
26422 if (nassociated_boundaries > 0)
26423 {
26424 Flat_packed_unsigneds.push_back(1);
26425#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26427 "The element is a boundary element");
26428#endif
26429 Flat_packed_unsigneds.push_back(nassociated_boundaries);
26430#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26431 std::stringstream junk;
26432 junk << "The elements is associated to " << nassociated_boundaries
26433 << " boundaries";
26434 Flat_packed_unsigneds_string.push_back(junk.str());
26435#endif
26436
26437 // Package the ids of the associated boundaries and the
26438 // corresponding face index for each boundary (if the element is a
26439 // corner element, it will have two faces associated to the
26440 // boundary)
26441 for (unsigned i = 0; i < nassociated_boundaries; i++)
26442 {
26443 unsigned b = associated_boundaries[i];
26444 Flat_packed_unsigneds.push_back(b);
26445#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26446 std::stringstream junk;
26447 junk << "Element associated to boundary " << b << " of "
26448 << nassociated_boundaries << " total associated boundaries";
26449 Flat_packed_unsigneds_string.push_back(junk.str());
26450#endif
26451 unsigned f = face_index_on_boundary[i];
26452 Flat_packed_unsigneds.push_back(f);
26453#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26454 std::stringstream junk2;
26455 junk2 << "Face index " << f << " for associated boundary " << b;
26456 Flat_packed_unsigneds_string.push_back(junk2.str());
26457#endif
26458 }
26459
26460 // If the element is associated to any boundary then we should
26461 // check if the mesh has regions, if that is the case then we need
26462 // to check to which region the boundary element does belong
26463
26464 // If the mesh has regions we should look for the element
26465 // associated to a boundary and a specified region
26466 Vector<Vector<unsigned>> associated_boundaries_and_regions;
26467 Vector<unsigned> face_index_on_boundary_and_region;
26468
26469 // Now check for the case when we have regions in the mesh
26470 const unsigned n_regions = this->nregion();
26471 if (n_regions > 1)
26472 {
26473 // Used to count the number of faces associated with
26474 // boundary-regions
26475 unsigned counter_face_indexes_in_regions = 0;
26476 // Loop over the boundaries
26477 for (unsigned b = 0; b < nbound; b++)
26478 {
26479 // Go through each region by getting the region id
26480 for (unsigned i_reg = 0; i_reg < n_regions; i_reg++)
26481 {
26482 // Get thre region id associated with the (i_reg)-th region
26483 const unsigned region_id =
26484 static_cast<unsigned>(this->Region_attribute[i_reg]);
26485
26486 // Loop over all elements associated with the current boundary
26487 // and the i_reg-th region and check if the element is part of
26488 // any region
26489 const unsigned nele_in_region =
26490 this->nboundary_element_in_region(b, region_id);
26491 for (unsigned ee = 0; ee < nele_in_region; ee++)
26492 {
26493 // Check if the boundary-region element is the same as the
26494 // element
26495 if (ele_pt ==
26496 this->boundary_element_in_region_pt(b, region_id, ee))
26497 {
26498 // Storage for the boundary and region associated to the
26499 // element
26500 Vector<unsigned> bound_and_region(2);
26501
26502 // Keep track of the boundaries associated to the element
26503 bound_and_region[0] = b;
26504 // Keep track of the regions associated to the element
26505 bound_and_region[1] = region_id;
26506 // Add the boundaries and regions in the storage to be
26507 // sent to other processors
26508 associated_boundaries_and_regions.push_back(bound_and_region);
26509 // Get the face index and keep track of it
26510 face_index_on_boundary_and_region.push_back(
26511 this->face_index_at_boundary_in_region(b, region_id, ee));
26512
26513 // Increase the number of faces of the element associated
26514 // to boundary-regions
26515 counter_face_indexes_in_regions++;
26516
26517#ifdef PARANOID
26518 if (counter_face_indexes_in_regions > 2)
26519 {
26520 std::stringstream error_message;
26521 error_message << "A triangular element can not have more "
26522 "than two of its\n"
26523 << "faces on a boundary!!!\n\n";
26524 throw OomphLibError(error_message.str(),
26525 "RefineableTriangleMesh::get_required_"
26526 "elemental_information_helper()",
26527 OOMPH_EXCEPTION_LOCATION);
26528 } // if (counter_face_indexes_in_regions > 2)
26529#endif
26530
26531 } // The element is a boundary-region element
26532
26533 } // for (ee < nele_in_region)
26534
26535 } // for (i_reg < n_regions)
26536
26537 } // for (b < nbound)
26538
26539 } // if (n_regions > 1)
26540
26541 // Now package the info. to be sent to other processors
26542 const unsigned nassociated_boundaries_and_regions =
26543 associated_boundaries_and_regions.size();
26544 if (nassociated_boundaries_and_regions > 0)
26545 {
26546 Flat_packed_unsigneds.push_back(1);
26547#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26549 "The element is associated to boundaries and regions");
26550#endif
26551
26552 Flat_packed_unsigneds.push_back(nassociated_boundaries_and_regions);
26553#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26554 std::stringstream junk;
26555 junk << "The element is associated to "
26556 << nassociated_boundaries_and_regions << " boundaries-regions";
26557 Flat_packed_unsigneds_string.push_back(junk.str());
26558#endif
26559
26560 // Package the ids of the associated boundaries, regions and the
26561 // corresponding face index for each boundary-region (if the
26562 // element is a corner element, it will have two faces
26563 // associated to the boundary-region)
26564 for (unsigned i = 0; i < nassociated_boundaries_and_regions; i++)
26565 {
26566 const unsigned b = associated_boundaries_and_regions[i][0];
26567 Flat_packed_unsigneds.push_back(b);
26568#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26569 std::stringstream junk;
26570 junk << "Element associated to boundary " << b << " of "
26571 << nassociated_boundaries_and_regions
26572 << " total associated boundaries-regions";
26573 Flat_packed_unsigneds_string.push_back(junk.str());
26574#endif
26575
26576 const unsigned r = associated_boundaries_and_regions[i][1];
26577 Flat_packed_unsigneds.push_back(r);
26578#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26579 std::stringstream junk2;
26580 junk2 << "Element associated to region " << r << " of "
26581 << nassociated_boundaries_and_regions
26582 << " total associated boundaries-regions";
26583 Flat_packed_unsigneds_string.push_back(junk2.str());
26584#endif
26585
26586 const unsigned f = face_index_on_boundary_and_region[i];
26587 Flat_packed_unsigneds.push_back(f);
26588#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26589 std::stringstream junk3;
26590 junk3 << "Face index " << f << " for associated boundary-region ("
26591 << b << "-" << r << ")";
26592 Flat_packed_unsigneds_string.push_back(junk3.str());
26593#endif
26594 } // for (i < nassociated_boundaries_and_regions)
26595 } // if (nassociated_boundaries_and_regions > 0)
26596 else
26597 {
26598 Flat_packed_unsigneds.push_back(0);
26599#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26601 "The element is NOT associated to boundaries and regions");
26602#endif
26603 } // else if (nassociated_boundaries_and_regions > 0)
26604 }
26605 else
26606 {
26607 Flat_packed_unsigneds.push_back(0);
26608#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26610 "The element is not associated to any original boundary");
26611#endif
26612 }
26613
26614 // ------------------------------------------------------------
26615 // Now review if the element is associated to a shared boundary
26616
26617 // Store the shared boundaries, and therefore the face indexes
26618 // associated to the element
26619 Vector<unsigned> associated_shared_boundaries;
26620 Vector<unsigned> face_index_on_shared_boundary;
26621
26622 // Get the shared boundaries in this processor
26623 Vector<unsigned> my_rank_shared_boundaries_ids;
26624 this->shared_boundaries_in_this_processor(my_rank_shared_boundaries_ids);
26625
26626 // Get the number of shared boundaries
26627 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
26628 // Loop over the shared boundaries
26629 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
26630 {
26631 // Get the boundary id
26632 const unsigned sb = my_rank_shared_boundaries_ids[i];
26633
26634 // Get the number of elements associated to shared boundary sb
26635 const unsigned nboundary_ele = this->nshared_boundary_element(sb);
26636 for (unsigned e = 0; e < nboundary_ele; e++)
26637 {
26638 if (ele_pt == this->shared_boundary_element_pt(sb, e))
26639 {
26640 // Keep track of the boundaries associated to the element
26641 associated_shared_boundaries.push_back(sb);
26642 // Get the face index
26643 face_index_on_shared_boundary.push_back(
26644 this->face_index_at_shared_boundary(sb, e));
26645 }
26646 } // (e < nboundary_ele)
26647 } // (i < nmy_rank_shd_bnd)
26648
26649 // If the element is associated to a shared boundary then package
26650 // all the relevant info
26651 const unsigned nassociated_shared_boundaries =
26652 associated_shared_boundaries.size();
26653 if (nassociated_shared_boundaries > 0)
26654 {
26655 Flat_packed_unsigneds.push_back(3);
26656#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26658 "The element is a shared boundary element");
26659#endif
26660 Flat_packed_unsigneds.push_back(nassociated_shared_boundaries);
26661#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26662 std::stringstream junk;
26663 junk << "The elements is associated to " << nassociated_shared_boundaries
26664 << "shared boundaries";
26665 Flat_packed_unsigneds_string.push_back(junk.str());
26666#endif
26667
26668 // Package the ids of the associated boundaries
26669 for (unsigned i = 0; i < nassociated_shared_boundaries; i++)
26670 {
26671 const unsigned b = associated_shared_boundaries[i];
26672 Flat_packed_unsigneds.push_back(b);
26673#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26674 std::stringstream junk;
26675 junk << "Element associated to shared boundary " << b << " of "
26676 << nassociated_shared_boundaries << " total associated boundaries";
26677 Flat_packed_unsigneds_string.push_back(junk.str());
26678#endif
26679
26680 const unsigned f = face_index_on_shared_boundary[i];
26681 Flat_packed_unsigneds.push_back(f);
26682#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26683 std::stringstream junk2;
26684 junk2 << "Face index " << f << " for associated shared boundary " << b;
26685 Flat_packed_unsigneds_string.push_back(junk2.str());
26686#endif
26687 }
26688 }
26689 else
26690 {
26691 Flat_packed_unsigneds.push_back(0);
26692#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26694 "The element is not associated to any shared boundary");
26695#endif
26696 }
26697
26698 // Now check if the element is haloed with any processor
26699
26700 // Store the index of the haloed element with the jproc
26701 Vector<Vector<unsigned>> index_haloed(nproc);
26702
26703 // Loop over the processors
26704 for (unsigned jproc = 0; jproc < nproc; jproc++)
26705 {
26706 // Get the number of haloed elements with jproc
26707 const unsigned n_haloed_jproc = f_haloed_ele_pt[jproc].size();
26708 // Loop over the haloed elements with jproc
26709 for (unsigned ihd = 0; ihd < n_haloed_jproc; ihd++)
26710 {
26711 // Is a haloed element?
26712 if (ele_pt == f_haloed_ele_pt[jproc][ihd])
26713 {
26714 // Store the haloed index with the jproc processor
26715 index_haloed[jproc].push_back(ihd);
26716 // Break the searching with the jproc processor
26717 break;
26718 } // if (ele_pt == f_haloed_ele_pt[jproc][ihd])
26719
26720 } // for (ihd < n_haloed_jproc)
26721
26722 } // for (jproc < nproc)
26723
26724 // Send the haloed info.
26725 // Loop over the processors
26726 for (unsigned jproc = 0; jproc < nproc; jproc++)
26727 {
26728 // Is the element haloed with the jproc processor
26729 const unsigned n_index_haloed_jproc = index_haloed[jproc].size();
26730 Flat_packed_unsigneds.push_back(n_index_haloed_jproc);
26731#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26733 "The number of haloed indexes the element is with processor jproc");
26734#endif
26735 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
26736 {
26737 Flat_packed_unsigneds.push_back(index_haloed[jproc][ihd]);
26738#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26740 "The haloed index of the element with jproc");
26741#endif
26742 } // for (ihd < n_index_haloed_jproc)
26743
26744 } // for (jproc < nproc)
26745 }
26746
26747 //======================================================================
26748 /// Helper function to add nodes on a new domain as a result of
26749 /// load balance
26750 //======================================================================
26751 template<class ELEMENT>
26753 unsigned& iproc,
26754 Vector<Vector<FiniteElement*>>& f_halo_ele_pt,
26755 Vector<Node*>& new_nodes_on_domain,
26756 Node* nod_pt)
26757 {
26758 // Attempt to add this node to the new domain
26759 const unsigned nnew_nodes_on_domain = new_nodes_on_domain.size();
26760 const unsigned new_added_node_index =
26761 this->try_to_add_node_pt_load_balance(new_nodes_on_domain, nod_pt);
26762
26763 // If it was added then the new index should match the size of the storage
26764 if (new_added_node_index == nnew_nodes_on_domain)
26765 {
26766 Flat_packed_unsigneds.push_back(1);
26767
26768#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26769 std::stringstream junk;
26770 junk << "Node needs to be constructed [size="
26771 << Flat_packed_unsigneds.size() << "]; last entry: "
26773 Flat_packed_unsigneds_string.push_back(junk.str());
26774#endif
26775
26776 // This helper function gets all the required information for the
26777 // specified node and stores it into MPI-sendable information
26778 // so that a new copy can be made on the receiving process
26779 get_required_nodal_information_load_balance_helper(
26780 f_halo_ele_pt, iproc, nod_pt);
26781 }
26782 else // It was already added
26783 {
26784 Flat_packed_unsigneds.push_back(0);
26785#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26786 std::stringstream junk;
26787 junk << "Node was already added [size=" << Flat_packed_unsigneds.size()
26788 << "]; last entry: "
26790
26791 Flat_packed_unsigneds_string.push_back(junk.str());
26792#endif
26793
26794 // This node has been already added, so tell the other process
26795 // its index in the equivalent storage
26796 Flat_packed_unsigneds.push_back(new_added_node_index);
26797#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26798 Flat_packed_unsigneds_string.push_back("new added node index");
26799#endif
26800 }
26801 }
26802
26803 //======start of get_required_nodal_information_load_balance_helper=======
26804 /// Helper function to get the required nodal information from an
26805 /// haloed node so that a fully-functional halo node (and therefore element)
26806 /// can be created on the receiving process
26807 //========================================================================
26808 template<class ELEMENT>
26811 Vector<Vector<FiniteElement*>>& f_halo_ele_pt,
26812 unsigned& iproc,
26813 Node* nod_pt)
26814 {
26815 unsigned my_rank = this->communicator_pt()->my_rank();
26816 const unsigned nproc = this->communicator_pt()->nproc();
26817
26818 // Tell the halo copy of this node how many values there are
26819 // [NB this may be different for nodes within the same element, e.g.
26820 // when using Lagrange multipliers]
26821 unsigned n_val = nod_pt->nvalue();
26822 Flat_packed_unsigneds.push_back(n_val);
26823#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26824 Flat_packed_unsigneds_string.push_back("Number of values");
26825#endif
26826
26827 unsigned n_dim = nod_pt->ndim();
26828
26829 // Default number of previous values to 1
26830 unsigned n_prev = 1;
26831 if (this->Time_stepper_pt != 0)
26832 {
26833 // Add number of history values to n_prev
26834 n_prev = this->Time_stepper_pt->ntstorage();
26835 }
26836
26837 // -----------------------------------------------------
26838 // Is the node on an original boundary?
26839 // Store the original boundaries where the node may be
26840 Vector<unsigned> original_boundaries;
26841 // Loop over the original boundaries of the mesh and check if live
26842 // on one of them
26843 const unsigned n_bnd = this->initial_shared_boundary_id();
26844 for (unsigned bb = 0; bb < n_bnd; bb++)
26845 {
26846 // Which boundaries (could be more than one) is it on?
26847 if (nod_pt->is_on_boundary(bb))
26848 {
26849 original_boundaries.push_back(bb);
26850 }
26851 }
26852
26853 const unsigned n_original_boundaries = original_boundaries.size();
26854 // Is the node on any original boundary?
26855 if (n_original_boundaries > 0)
26856 {
26857 // Indicate that the node is on an original boundary
26858 Flat_packed_unsigneds.push_back(2);
26859#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26861 "Node is on the original boundaries");
26862#endif
26863
26864 Flat_packed_unsigneds.push_back(n_original_boundaries);
26865#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26866 std::stringstream junk;
26867 junk << "Node is on " << n_original_boundaries << " original boundaries";
26868 Flat_packed_unsigneds_string.push_back(junk.str());
26869#endif
26870
26871 // Loop over the original boundaries the node is on
26872 for (unsigned i = 0; i < n_original_boundaries; i++)
26873 {
26874 Flat_packed_unsigneds.push_back(original_boundaries[i]);
26875#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26876 std::stringstream junk;
26877 junk << "Node is on boundary " << original_boundaries[i] << " of "
26878 << nb;
26879 Flat_packed_unsigneds_string.push_back(junk.str());
26880#endif
26881 // Get the boundary coordinate of the node
26882 Vector<double> zeta(1);
26883 nod_pt->get_coordinates_on_boundary(original_boundaries[i], zeta);
26884 Flat_packed_doubles.push_back(zeta[0]);
26885 }
26886 }
26887 else
26888 {
26889 // Indicate that the node is NOT on an original boundary
26890 Flat_packed_unsigneds.push_back(0);
26891#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26893 "Node is on any original boundary");
26894#endif
26895 }
26896
26897 // -------------------------------------------------------
26898 // Is the node on shared boundaries?
26899 bool node_on_shared_boundary = false;
26900 // Loop over the shared boundaries with the iproc processors and
26901 // check if live on one of them
26902 const unsigned n_shd_bnd = this->nshared_boundaries(my_rank, iproc);
26903 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26904 {
26905 // Get the boundary id
26906 unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26907 // Which boundaries (could be more than one) is it on?
26908 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26909 {
26910 node_on_shared_boundary = true;
26911 break;
26912 }
26913 }
26914
26915 // If the node live on any of the shared boundaries with the iproc
26916 // processor then just get the node number according to the
26917 // sorted_shared_boundary_node_pt() scheme and send it accross
26918 if (node_on_shared_boundary)
26919 {
26920 Flat_packed_unsigneds.push_back(1);
26921#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26922 Flat_packed_unsigneds_string.push_back("Node is on shared boundary");
26923#endif
26924
26925 // Store the shared boundaries where the node is on
26926 Vector<unsigned> shd_boundaries;
26927 // Loop over the shared boundaries with the iproc processor
26928 for (unsigned bb = 0; bb < n_shd_bnd; bb++)
26929 {
26930 // Get the boundary id
26931 const unsigned i_bnd = this->shared_boundaries_ids(my_rank, iproc, bb);
26932 // Which boundaries (could be more than one) is it on?
26933 if (this->is_node_on_shared_boundary(i_bnd, nod_pt))
26934 {
26935 shd_boundaries.push_back(i_bnd);
26936 }
26937 }
26938
26939 // Get the number of shared boundaries the node is on
26940 const unsigned n_shd_bnd_is_on = shd_boundaries.size();
26941 // Send the number of shared boundaries the node is on
26942 Flat_packed_unsigneds.push_back(n_shd_bnd_is_on);
26943#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26944 std::stringstream junk;
26945 junk << "Node is on " << n_shd_bnd_is_on << " shared boundaries";
26946 Flat_packed_unsigneds_string.push_back(junk.str());
26947#endif
26948
26949 // Loop over the shared boundaries to send their ids
26950 for (unsigned i = 0; i < n_shd_bnd_is_on; i++)
26951 {
26952 Flat_packed_unsigneds.push_back(shd_boundaries[i]);
26953#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
26954 std::stringstream junk;
26955 junk << "Node is on boundary " << shd_boundaries[i] << " of " << nb;
26956 Flat_packed_unsigneds_string.push_back(junk.str());
26957#endif
26958 }
26959
26960 // Given that the node is on at least one boundary get the index
26961 // of the node in one of the boundaries and send this index
26962 unsigned shared_boundary_id = shd_boundaries[0];
26963 // Get the number of nodes on the given shared boundary
26964 const unsigned n_nodes_on_shared_boundary =
26965 nsorted_shared_boundary_node(shared_boundary_id);
26966 // Store the index of the node on the shared boundary
26967 unsigned index_node_on_shared_boundary;
26968#ifdef PARANOID
26969 // Flag to know if the node has been found
26970 bool found_index_node_on_shared_boundary = false;
26971#endif
26972 // Loop over the nodes on the shared boundary to find the node
26973 for (unsigned i = 0; i < n_nodes_on_shared_boundary; i++)
26974 {
26975 // Get the i-th node on the shared boundary
26976 Node* shared_node_pt =
26977 sorted_shared_boundary_node_pt(shared_boundary_id, i);
26978 // Is the node we are looking for
26979 if (shared_node_pt == nod_pt)
26980 {
26981 // Store the index
26982 index_node_on_shared_boundary = i;
26983#ifdef PARANOID
26984 // Mark as found
26985 found_index_node_on_shared_boundary = true;
26986#endif
26987 break; // break
26988 }
26989
26990 } // for (i < nnodes_on_shared_boundary)
26991
26992#ifdef PARANOID
26993 if (!found_index_node_on_shared_boundary)
26994 {
26995 std::ostringstream error_message;
26996 error_message << "The index of the node on boundary ("
26997 << shared_boundary_id << ") was not found.\n"
26998 << "The node coordinates are (" << nod_pt->x(0) << ","
26999 << nod_pt->x(1) << ").\n";
27000 throw OomphLibError(
27001 error_message.str(),
27002 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27003 OOMPH_EXCEPTION_LOCATION);
27004 }
27005#endif
27006 // Send the index of the node on the shared boundary
27007 Flat_packed_unsigneds.push_back(index_node_on_shared_boundary);
27008#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27009 std::stringstream junk2;
27010 junk2 << "Node index on boundary " << boundaries[0] << " is "
27011 << index_node_on_shared_boundary;
27012 Flat_packed_unsigneds_string.push_back(junk2.str());
27013#endif
27014
27015 } // if (node_on_shared_boundary)
27016 else
27017 {
27018 // The node is not on a shared boundary
27019 Flat_packed_unsigneds.push_back(0);
27020#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27022 "Node is not on a shared boundary");
27023#endif
27024 }
27025
27026 // ----------------------------------------------------------------
27027 // Is the node on any shared boundary where the receiver processor
27028 // is not involved?
27029
27030 // Now check if the node is on a shared boundary created by the
27031 // current processor (my_rank) and other processor different that
27032 // the iproc processor. This info. will help to complete the sending
27033 // of halo(ed) information between processors
27034
27035 // Flag to know if the node is on a shared boundary with other
27036 // processor
27037 bool node_on_shared_boundary_with_other_processors = false;
27038 // Count the number of other shared boundaries it could be on
27039 unsigned nshared_boundaries_with_other_processors_have_node = 0;
27040
27041 // Loop over the shared boundaries of the sent processor (my_rank)
27042 // and other processors (jproc)
27043 for (unsigned jproc = 0; jproc < nproc; jproc++)
27044 {
27045 // Do not search with the iproc processor, that was done before
27046 // above
27047 if (jproc != iproc)
27048 {
27049 // Get the number of shared boundaries with the jproc processor
27050 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27051 // Loop over the shared boundaries
27052 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27053 {
27054 // Get the boundary id
27055 const unsigned j_shd_bnd =
27056 this->shared_boundaries_ids(my_rank, jproc, bb);
27057 // Is the node part of this boundary?
27058 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27059 {
27060 // DEBP("Sending to");
27061 // DEBP(iproc);
27062 // DEBP("Pair of procs where other shared");
27063 // DEBP(my_rank);
27064 // DEBP(jproc);
27065 // DEBP(i_bnd);
27066 node_on_shared_boundary_with_other_processors = true;
27067 // Increase the counter for the number of shared boundaries
27068 // with other processors the node is on
27069 nshared_boundaries_with_other_processors_have_node++;
27070 } // if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt)
27071
27072 } // for (bb<n_jshd_bnd)
27073
27074 } // if (jproc != iproc)
27075
27076 } // for (jproc < nproc)
27077
27078 // If the node is on a shared boundary with another processor
27079 // (my_rank, jproc), then send the flag and look for the info.
27080 if (node_on_shared_boundary_with_other_processors)
27081 {
27082 Flat_packed_unsigneds.push_back(4);
27083#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27085 "Node is on shared boundary no related with the received processor: 4");
27086#endif
27087
27088 // The number of packages of information that will be sent to the
27089 // "iproc" processor. This helps to know how many packages of data
27090 // read from the received processor
27091 Flat_packed_unsigneds.push_back(
27092 nshared_boundaries_with_other_processors_have_node);
27093#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27094 std::stringstream junk;
27095 junk << "Number of other shared boundaries that the node is on: "
27096 << nshared_boundaries_with_other_processors_have_node;
27097 Flat_packed_unsigneds_string.push_back(junk.str());
27098#endif
27099
27100 // Counter to ensure that the correct number of data has been sent
27101 unsigned counter_shd_bnd_with_other_procs_have_node = 0;
27102 // Loop over the shared boundaries with other processors and get:
27103 // 1) The processors defining the shared boundary
27104 // 2) The shared boundary id
27105 // 3) The index of the node on the shared boundary
27106 Vector<unsigned> other_processor_1;
27107 Vector<unsigned> other_processor_2;
27108 Vector<unsigned> shd_bnd_ids;
27109 Vector<unsigned> indexes;
27110 // Loop over the processors again
27111 for (unsigned jproc = 0; jproc < nproc; jproc++)
27112 {
27113 // Do not search with the iproc processor, that was done before
27114 // above
27115 if (jproc != iproc)
27116 {
27117 // Get the number of shared boundaries with the jproc
27118 // processor
27119 const unsigned n_jshd_bnd = this->nshared_boundaries(my_rank, jproc);
27120 for (unsigned bb = 0; bb < n_jshd_bnd; bb++)
27121 {
27122 // Get the boundary id
27123 const unsigned j_shd_bnd =
27124 this->shared_boundaries_ids(my_rank, jproc, bb);
27125 // Is the node part of this boundary?
27126 if (this->is_node_on_shared_boundary(j_shd_bnd, nod_pt))
27127 {
27128 // Include the first processor
27129 other_processor_1.push_back(my_rank);
27130 // Include the second processor
27131 other_processor_2.push_back(jproc);
27132 // Include the shared boundary id
27133 shd_bnd_ids.push_back(j_shd_bnd);
27134 // Increase the counter for found shared boundaries with
27135 // other processors
27136 counter_shd_bnd_with_other_procs_have_node++;
27137 }
27138
27139 } // for (bb < nshared_bnd)
27140
27141 } // if (jproc != iproc)
27142
27143 } // for (jproc < nproc)
27144
27145 // Get the indexes of the node on all the shared boundaries where
27146 // it was found
27147 const unsigned n_other_processors = other_processor_1.size();
27148 // Loop over the processors where the node was found
27149 for (unsigned i = 0; i < n_other_processors; i++)
27150 {
27151 // Get the shared boundary id
27152 unsigned shd_bnd_id = shd_bnd_ids[i];
27153 // Get the number of nodes on that shared boundary
27154 const unsigned n_nodes_on_shd_bnd =
27155 nsorted_shared_boundary_node(shd_bnd_id);
27156
27157#ifdef PARANOID
27158 bool found_index_node_on_shared_boundary = false;
27159#endif
27160 for (unsigned i = 0; i < n_nodes_on_shd_bnd; i++)
27161 {
27162 // Get the i-th shared boundary node
27163 Node* shared_node_pt = sorted_shared_boundary_node_pt(shd_bnd_id, i);
27164 // Is the same node?
27165 if (shared_node_pt == nod_pt)
27166 {
27167 // DEBP(i_node);
27168 // DEBP(nod_pt->x(0));
27169 // DEBP(nod_pt->x(1));
27170 // Include the index of the node
27171 indexes.push_back(i);
27172#ifdef PARANOID
27173 // Mark as found the node
27174 found_index_node_on_shared_boundary = true;
27175#endif
27176 break;
27177 } // if (shared_node_pt == nod_pt)
27178
27179 } // for (i < n_nodes_on_shd_bnd)
27180
27181#ifdef PARANOID
27182 if (!found_index_node_on_shared_boundary)
27183 {
27184 std::ostringstream error_message;
27185 error_message << "The index of the node on boundary (" << shd_bnd_id
27186 << "), shared by other processors\nwas not found.\n"
27187 << "The node coordinates are (" << nod_pt->x(0) << ","
27188 << nod_pt->x(1) << ").\n";
27189 throw OomphLibError(
27190 error_message.str(),
27191 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27192 OOMPH_EXCEPTION_LOCATION);
27193 }
27194#endif
27195 } // for (i < n_other_processors)
27196
27197 // Now send the info. but first check that the number of found
27198 // nodes be the same that the previously found shared boundaries
27199 // with the node
27200#ifdef PARANOID
27201 if (counter_shd_bnd_with_other_procs_have_node !=
27202 nshared_boundaries_with_other_processors_have_node)
27203 {
27204 std::ostringstream error_message;
27205 error_message << "The number of shared boundaries where the node is on "
27206 << "is different:\n"
27207 << "nshared_boundaries_with_other_processors_have_node: ("
27208 << nshared_boundaries_with_other_processors_have_node
27209 << ")\n"
27210 << "counter_shd_bnd_with_other_procs_have_node: ("
27211 << counter_shd_bnd_with_other_procs_have_node << ")\n";
27212 throw OomphLibError(
27213 error_message.str(),
27214 "RefineableTriangleMesh::get_required_nodal_information_helper()",
27215 OOMPH_EXCEPTION_LOCATION);
27216 } // if (counter_shd_bnd_with_other_procs_have_node !=
27217 // nshared_boundaries_with_other_processors_have_node)
27218#endif
27219
27220 // Loop over the info. to send it
27221 for (unsigned i = 0; i < n_other_processors; i++)
27222 {
27223 Flat_packed_unsigneds.push_back(other_processor_1[i]);
27224#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27225 std::stringstream junk1;
27226 junk1 << "Processor where the other shared boundary "
27227 << "has the node: " << other_processor_1[i];
27228 Flat_packed_unsigneds_string.push_back(junk1.str());
27229#endif
27230
27231 Flat_packed_unsigneds.push_back(other_processor_2[i]);
27232#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27233 std::stringstream junk2;
27234 junk2 << "Processor where the other shared boundary "
27235 << "has the node: " << other_processor_2[i];
27236 Flat_packed_unsigneds_string.push_back(junk2.str());
27237#endif
27238
27239 Flat_packed_unsigneds.push_back(shd_bnd_ids[i]);
27240#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27241 std::stringstream junk3;
27242 junk3 << "Other shared boundary id where the node is on"
27243 << boundaries[i];
27244 Flat_packed_unsigneds_string.push_back(junk3.str());
27245#endif
27246
27247 Flat_packed_unsigneds.push_back(indexes[i]);
27248#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27249 std::stringstream junk4;
27250 junk4 << "Node index on other shared boundary " << boundaries[i]
27251 << " is " << indexes[i];
27252 Flat_packed_unsigneds_string.push_back(junk4.str());
27253#endif
27254
27255 } // for (i < n_other_processors)
27256
27257 } // if (node_on_shared_boundary_with_other_processors)
27258 else
27259 {
27260 Flat_packed_unsigneds.push_back(0);
27261#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27263 "Node is on any shared boundary with other processors");
27264#endif
27265 } // else if (node_on_shared_boundary_with_other_processors)
27266
27267 // It may still be possible that the node be shared with the
27268 // processor that receives the info. but it is neither on shared
27269 // boundary with the receiver processor nor on a shared boundary
27270 // with others processors. Think in the next case:
27271
27272 // |-----|-----| - The elements in processor 3 need to be sent to
27273 // | 4 | 3 | processor 1, and that is all
27274 // |-----*-----| - When processor 1 receives the data from node (*)
27275 // | 1 | 2 | it just RE-CREATES it becasuse it is does not know
27276 // |-----|-----| that the node is also on the shared boundary that
27277 // processor 1 and 2 or processor 1 and 4 share.
27278
27279 // This problem become even worse if there would be more processors
27280 // between processor 3 and 2, or/and processor 3 and 4. Think in
27281 // triangles sharing the node (*)
27282
27283 // To solve this check if the node that we are trying to send is
27284 // part of the halo elements of the curreent processor (my_rank)
27285 // with any other processor (we need to check with all the
27286 // processors and not just with the processors to which we will send
27287 // to cover more cases)
27288
27289 // Store the halo element number with jproc where the node was found
27290 Vector<Vector<unsigned>> halo_element_number(nproc);
27291 // Store the node number on the halo element where the node was found
27292 Vector<Vector<unsigned>> halo_node_number_in_halo_element(nproc);
27293
27294 // Loop over the processor
27295 for (unsigned jproc = 0; jproc < nproc; jproc++)
27296 {
27297 // Get the number of halo elements with the jproc processor
27298 const unsigned n_halo_jproc = f_halo_ele_pt[jproc].size();
27299 // Loop over the halo elements
27300 for (unsigned jh = 0; jh < n_halo_jproc; jh++)
27301 {
27302 FiniteElement* halo_ele_pt = f_halo_ele_pt[jproc][jh];
27303 // Get the number of nodes of the halo element
27304 const unsigned n_node = halo_ele_pt->nnode();
27305 // Loop over the nodes
27306 for (unsigned n = 0; n < n_node; n++)
27307 {
27308 // Is the node part of the ih-th halo element with jproc
27309 if (nod_pt == halo_ele_pt->node_pt(n))
27310 {
27311 halo_element_number[jproc].push_back(jh);
27312 halo_node_number_in_halo_element[jproc].push_back(n);
27313 // break with the nodes, no need to look for more nodes in
27314 // the element
27315 break;
27316 } // if (nod_pt == halo_ele_pt->node_pt(n))
27317
27318 } // for (n < n_node)
27319
27320 } // for (jh < n_halo_jproc)
27321
27322 } // for (jproc < nproc)
27323
27324 // Send the info. related with if the node is on halo elements with
27325 // any processor
27326
27327 // Loop over the processors
27328 for (unsigned jproc = 0; jproc < nproc; jproc++)
27329 {
27330 // Get the number of halo elements with jproc processor where the
27331 // node is
27332 const unsigned n_jproc_halo_ele_node_is_on =
27333 halo_element_number[jproc].size();
27334 // Send the number of halo elements with jproc where the node is
27335 Flat_packed_unsigneds.push_back(n_jproc_halo_ele_node_is_on);
27336#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27337 std::stringstream junk5;
27338 junk5 << "Node is on " << n_jproc_halo_ele_node_is_on << " halo "
27339 << "elements with " << jproc << "-th processor";
27340 Flat_packed_unsigneds_string.push_back(junk5.str());
27341#endif
27342 // Send the halo elements indexes (which will be haloed elements
27343 // indexes in the receiver processor), and the indexes of the
27344 // nodes in each halo element
27345 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
27346 {
27347 // The halo element index
27348 const unsigned halo_element_index = halo_element_number[jproc][i];
27349 Flat_packed_unsigneds.push_back(halo_element_index);
27350#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27351 std::stringstream junk6;
27352 junk6 << "Halo element index is (" << halo_element_index
27353 << ") with processor (" << jproc << ")";
27354 Flat_packed_unsigneds_string.push_back(junk6.str());
27355#endif
27356 // The node index on the halo element
27357 const unsigned node_index = halo_node_number_in_halo_element[jproc][i];
27358 Flat_packed_unsigneds.push_back(node_index);
27359#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27360 std::stringstream junk7;
27361 junk7 << "The node index on the halo element index is (" << node_index;
27362 Flat_packed_unsigneds_string.push_back(junk7.str());
27363#endif
27364
27365 } // for (i < n_jproc_halo_ele_node_is_on)
27366
27367 } // for (jproc < nproc)
27368
27369 // Now check if it is required to send the info. of the node. If the
27370 // node is not on a shared boundary with the iproc processor then we
27371 // need to send the info.
27372
27373 // Flag to indicate if it is on a halo element with the iproc
27374 // processor. If this flag is true then there is no need to send the
27375 // info. to create the node, in the receiver processor the info is
27376 // copied from the indicated haloed element-node
27377 bool on_halo_element_with_iproc_processor = false;
27378 if (halo_element_number[iproc].size() > 0)
27379 {
27380 on_halo_element_with_iproc_processor = true;
27381 } // if (halo_element_number[iproc].size() > 0)
27382
27383 // if (!node_on_shared_boundary)
27384 if (!node_on_shared_boundary && !on_halo_element_with_iproc_processor)
27385 {
27386 // Send all the info. to create it
27387
27388 // Is the Node algebraic? If so, send its ref values and
27389 // an indication of its geometric objects if they are stored
27390 // in the algebraic mesh
27391 AlgebraicNode* alg_nod_pt = dynamic_cast<AlgebraicNode*>(nod_pt);
27392 if (alg_nod_pt != 0)
27393 {
27394 // The external mesh should be algebraic
27395 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
27396
27397 // Get default node update function ID
27398 unsigned update_id = alg_nod_pt->node_update_fct_id();
27399 Flat_packed_unsigneds.push_back(update_id);
27400#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27401 Flat_packed_unsigneds_string.push_back("Alg Node update id");
27402#endif
27403
27404 // Get reference values at default...
27405 unsigned n_ref_val = alg_nod_pt->nref_value();
27406 Flat_packed_unsigneds.push_back(n_ref_val);
27407#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27408 Flat_packed_unsigneds_string.push_back("Alg Node n ref values");
27409#endif
27410 for (unsigned i_ref_val = 0; i_ref_val < n_ref_val; i_ref_val++)
27411 {
27412 Flat_packed_doubles.push_back(alg_nod_pt->ref_value(i_ref_val));
27413 }
27414
27415 // Access geometric objects at default...
27416 unsigned n_geom_obj = alg_nod_pt->ngeom_object();
27417 Flat_packed_unsigneds.push_back(n_geom_obj);
27418#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27419 Flat_packed_unsigneds_string.push_back("Alg Node n geom objects");
27420#endif
27421 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
27422 {
27423 GeomObject* geom_obj_pt = alg_nod_pt->geom_object_pt(i_geom);
27424
27425 // Check this against the stored geometric objects in mesh
27426 unsigned n_geom_list = alg_mesh_pt->ngeom_object_list_pt();
27427
27428 // Default found index to zero
27429 unsigned found_geom_object = 0;
27430 for (unsigned i_list = 0; i_list < n_geom_list; i_list++)
27431 {
27432 if (geom_obj_pt == alg_mesh_pt->geom_object_list_pt(i_list))
27433 {
27434 found_geom_object = i_list;
27435 }
27436 }
27437 Flat_packed_unsigneds.push_back(found_geom_object);
27438#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27439 Flat_packed_unsigneds_string.push_back("Found geom object");
27440#endif
27441 }
27442 } // (if alg_nod_pt!=0)
27443
27444 // Is it a SolidNode?
27445 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(nod_pt);
27446 if (solid_nod_pt != 0)
27447 {
27448 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
27449 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
27450 {
27451 for (unsigned t = 0; t < n_prev; t++)
27452 {
27453 Flat_packed_doubles.push_back(
27454 solid_nod_pt->variable_position_pt()->value(t, i_val));
27455 }
27456 }
27457
27458 Vector<double> values_solid_node;
27459 solid_nod_pt->add_values_to_vector(values_solid_node);
27460 const unsigned nvalues_solid_node = values_solid_node.size();
27461 Flat_packed_unsigneds.push_back(nvalues_solid_node);
27462#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27463 std::stringstream junk;
27464 junk << "Number of values solid node: " << nvalues_solid_node;
27465 Flat_packed_unsigneds_string.push_back(junk.str());
27466#endif
27467 for (unsigned i = 0; i < nvalues_solid_node; i++)
27468 {
27469 Flat_packed_doubles.push_back(values_solid_node[i]);
27470 }
27471 }
27472
27473 // Finally copy info required for all node types
27474 for (unsigned i_val = 0; i_val < n_val; i_val++)
27475 {
27476 for (unsigned t = 0; t < n_prev; t++)
27477 {
27478 Flat_packed_doubles.push_back(nod_pt->value(t, i_val));
27479 }
27480 }
27481
27482 // Now do positions
27483 for (unsigned idim = 0; idim < n_dim; idim++)
27484 {
27485 for (unsigned t = 0; t < n_prev; t++)
27486 {
27487 Flat_packed_doubles.push_back(nod_pt->x(t, idim));
27488 // DEBP(nod_pt->x(t,idim));
27489 }
27490 }
27491
27492 } // if (!node_on_shared_boundary && !on_halo_element_with_iproc_processor)
27493 }
27494
27495 //======================================================================
27496 /// Helper function to create elements on the loop
27497 /// process based on the info received in
27498 /// send_and_received_elements_nodes_info
27499 //======================================================================
27500 template<class ELEMENT>
27502 unsigned& iproc,
27503 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27504 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27505 received_old_haloed_element_pt,
27506 Vector<FiniteElement*>& new_elements_on_domain,
27507 Vector<Node*>& new_nodes_on_domain,
27508 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27509 other_proc_shd_bnd_node_pt,
27510 Vector<Vector<Vector<unsigned>>>& global_node_names,
27511 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27512 Vector<Node*>& global_shared_node_pt)
27513 {
27514#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27516 << " Bool: New element needs to be constructed "
27518 << std::endl;
27519#endif
27520
27522 {
27523 // Create a new element from the communicated values
27524 // and coords from the process that located zeta
27525 GeneralisedElement* new_el_pt = new ELEMENT;
27526
27527 // Add the new element to the mesh - Do not add the element yet
27528 // since no retained elements still need to be deleted
27529 // this->add_element_pt(new_el_pt);
27530
27531 // Cast to the FE pointer
27532 FiniteElement* f_el_pt = dynamic_cast<FiniteElement*>(new_el_pt);
27533
27534 // Add the element to the new elements in the domain container
27535 new_elements_on_domain.push_back(f_el_pt);
27536
27537 // Set any additional information for the element
27538 this->add_element_load_balance_helper(
27539 iproc, received_old_haloed_element_pt, f_el_pt);
27540
27541 // Add nodes to the new element
27542 unsigned n_node = f_el_pt->nnode();
27543 for (unsigned j = 0; j < n_node; j++)
27544 {
27545 Node* new_nod_pt = 0;
27546
27547 // Call the add halo node helper function
27548 add_received_node_load_balance_helper(new_nod_pt,
27549 f_haloed_ele_pt,
27550 received_old_haloed_element_pt,
27551 new_nodes_on_domain,
27552 other_proc_shd_bnd_node_pt,
27553 iproc,
27554 j,
27555 f_el_pt,
27556 global_node_names,
27557 node_name_to_global_index,
27558 global_shared_node_pt);
27559 }
27560 }
27561 else // the element already exists
27562 {
27563#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27565 << " Index of existing element "
27567 << std::endl;
27568#endif
27569
27570 // Incrase the index, we do anything else with the element
27572
27573 } // else the element already exists
27574 }
27575
27576 //========start of add_element_load_balance_helper=====================
27577 /// Helper function to create elements on the loop
27578 /// process based on the info received in
27579 /// send_and_received_elements_nodes_info
27580 /// This function is in charge of verify if the element is associated
27581 /// to a boundary and associate to it if that is the case
27582 //======================================================================
27583 template<class ELEMENT>
27585 const unsigned& iproc,
27586 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27587 received_old_haloed_element_pt,
27588 FiniteElement* ele_pt)
27589 {
27590 // Get the number of processors
27591 const unsigned nproc = this->communicator_pt()->nproc();
27592
27593#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27595 << " Bool: Element is associated to a boundary "
27597 << std::endl;
27598#endif
27599
27600 // Is on an original boundary?
27601 const unsigned is_on_original_boundary =
27603 if (is_on_original_boundary == 1)
27604 {
27605#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27607 << " How many boundaries are associated with the element "
27609 << std::endl;
27610#endif
27611 // Number of boundaries the element is associated with
27612 const unsigned nassociated_boundaries =
27614
27615 // Loop over the associated boundaries
27616 for (unsigned b = 0; b < nassociated_boundaries; b++)
27617 {
27618#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27620 << " Boundary associated to the element "
27622 << std::endl;
27623#endif
27624
27625 // The boundary id
27626 const unsigned bnd =
27628
27629#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27631 << " Face index of the element "
27633 << std::endl;
27634#endif
27635
27636 // The face index
27637 const unsigned face_index =
27639
27640 // Associate the element with the boundary and establish as many
27641 // face indexes it has
27642 this->Boundary_element_pt[bnd].push_back(ele_pt);
27643 this->Face_index_at_boundary[bnd].push_back(face_index);
27644
27645 } // (b < nassociated_boundaries)
27646
27647 // Here read the info. regarding the boundary-region of the element
27648#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27650 << " Bool: Element is associated to a boundary-region "
27652 << std::endl;
27653#endif
27654
27655 // Is the element associated to a boundary-region?
27657 {
27658#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27661 << " How many boundaries-regions are associated with the element "
27663 << std::endl;
27664#endif
27665 // Number of boundary-regions the element is associated
27666 const unsigned nassociated_boundaries_and_regions =
27668
27669 for (unsigned br = 0; br < nassociated_boundaries_and_regions; br++)
27670 {
27671#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27673 << " Boundary associated to the element "
27675 << std::endl;
27676#endif
27677 // The boundary id
27678 const unsigned bnd =
27680
27681#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27683 << " Region associated to the element "
27685 << std::endl;
27686#endif
27687 // The region id
27688 const unsigned region =
27690
27691#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27693 << " Face index of the element in boundary-region "
27695 << std::endl;
27696#endif
27697 const unsigned face_index =
27699
27700 // Associate the element with the boundary-regions and establish
27701 // as many face indexes it has
27702 this->Boundary_region_element_pt[bnd][region].push_back(ele_pt);
27703 this->Face_index_region_at_boundary[bnd][region].push_back(
27704 face_index);
27705
27706 } // for (br < nassociated_boundaries_and_regions)
27707
27708 } // Is the element associated with a boundary-region?
27709
27710 } // The element is associated with an original boundary
27711#ifdef PARANOID
27712 else
27713 {
27714 if (is_on_original_boundary != 0)
27715 {
27716 std::ostringstream error_message;
27717 error_message
27718 << "The current element is not on an original boundary, this should\n"
27719 << "be indicated by a zero flag. However, the read value for\n"
27720 << "that flag is (" << is_on_original_boundary << ").\n\n";
27721 throw OomphLibError(
27722 error_message.str(),
27723 "RefineableTriangleMesh::add_element_load_balance_helper()",
27724 OOMPH_EXCEPTION_LOCATION);
27725 } // if (is_on_shared_boundary != 0)
27726 }
27727#endif
27728
27729#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27731 << " Bool: Element is associated to a shared boundary "
27733 << std::endl;
27734#endif
27735
27736 // Is the element a shared boundary element?
27737 const unsigned is_on_shared_boundary =
27739 if (is_on_shared_boundary == 3)
27740 {
27741#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27744 << " How many shared boundaries are associated with the element "
27746 << std::endl;
27747#endif
27748
27749 // The number of shared boundaries the element is associated
27750 const unsigned nassociated_shared_boundaries =
27752
27753 // Loop over the associated shared boundaries
27754 for (unsigned b = 0; b < nassociated_shared_boundaries; b++)
27755 {
27756#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27758 << " Shared boundary associated to the element "
27760 << std::endl;
27761#endif
27762 const unsigned bnd =
27764
27765#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27768 << " Face index of the element associated to the shared boundary "
27770 << std::endl;
27771#endif
27772
27773 const unsigned face_index =
27775
27776 this->add_shared_boundary_element(bnd, ele_pt);
27777 this->add_face_index_at_shared_boundary(bnd, face_index);
27778
27779 } // (b < nassociated_shared_boundaries)
27780
27781 } // The element is associted with a shared boundary
27782#ifdef PARANOID
27783 else
27784 {
27785 if (is_on_shared_boundary != 0)
27786 {
27787 std::ostringstream error_message;
27788 error_message
27789 << "The current element is not on a shared boundary, this should\n"
27790 << "be indicated by a zero flag. However, the read value for\n"
27791 << "that flag is (" << is_on_shared_boundary << ").\n\n";
27792 throw OomphLibError(
27793 error_message.str(),
27794 "RefineableTriangleMesh::add_element_load_balance_helper()",
27795 OOMPH_EXCEPTION_LOCATION);
27796 } // if (is_on_shared_boundary != 0)
27797 }
27798#endif
27799
27800 // Now check if the element is a haloed element in the sender
27801 // processor with any other processor
27802
27803 // Loop over the processors
27804 for (unsigned jproc = 0; jproc < nproc; jproc++)
27805 {
27806#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27808 << " Bool: Number of haloed indexes of the element with the "
27809 << jproc << " processor: "
27811 << std::endl;
27812#endif
27813 // Is the element haloed with the jproc processor
27814 const unsigned n_index_haloed_jproc =
27816 // Loop over the number of haloed indexes
27817 for (unsigned ihd = 0; ihd < n_index_haloed_jproc; ihd++)
27818 {
27819#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27821 << " Bool: The haloed element index with the " << jproc
27822 << " processor: "
27824 << std::endl;
27825#endif
27826 const unsigned haloed_index =
27828
27829 // Set the halod element in the proper storage
27830 received_old_haloed_element_pt[iproc][jproc][haloed_index] = ele_pt;
27831
27832 } // for (ihd < n_index_haloed_jproc)
27833
27834 } // for (jproc < nproc)
27835 }
27836
27837 //======================================================================
27838 /// Helper function to add a new node from load balance
27839 //======================================================================
27840 template<class ELEMENT>
27842 Node*& new_nod_pt,
27843 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27844 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27845 received_old_haloed_element_pt,
27846 Vector<Node*>& new_nodes_on_domain,
27847 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27848 other_proc_shd_bnd_node_pt,
27849 unsigned& iproc,
27850 unsigned& node_index,
27851 FiniteElement* const& new_el_pt,
27852 Vector<Vector<Vector<unsigned>>>& global_node_names,
27853 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27854 Vector<Node*>& global_shared_node_pt)
27855 {
27856 // Given the node, received information about it from processor
27857 // iproc, construct it on the current process
27858#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27860 << " Bool: New node needs to be constructed "
27862 << std::endl;
27863#endif
27865 {
27866 // Construct a new node based upon sent information, or copy a node
27867 // from one of the shared boundaries
27868 construct_new_node_load_balance_helper(new_nod_pt,
27869 f_haloed_ele_pt,
27870 received_old_haloed_element_pt,
27871 new_nodes_on_domain,
27872 other_proc_shd_bnd_node_pt,
27873 iproc,
27874 node_index,
27875 new_el_pt,
27876 global_node_names,
27877 node_name_to_global_index,
27878 global_shared_node_pt);
27879 }
27880 else
27881 {
27882#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27884 << " Index of existing halo node "
27886 << std::endl;
27887#endif
27888 // The node already exist, copy it from the indicated position
27889
27890 // Get the node's index, and copy it
27891 new_nod_pt = new_nodes_on_domain
27893
27894 // Set the node in the current element
27895 new_el_pt->node_pt(node_index) = new_nod_pt;
27896 }
27897 }
27898
27899 //============start_of_construct_new_node_load_balance_helper()=========
27900 /// Helper function which constructs a new node (on an
27901 /// element) with the information sent from the load balance
27902 /// process
27903 //======================================================================
27904 template<class ELEMENT>
27906 Node*& new_nod_pt,
27907 Vector<Vector<FiniteElement*>>& f_haloed_ele_pt,
27908 Vector<Vector<std::map<unsigned, FiniteElement*>>>&
27909 received_old_haloed_element_pt,
27910 Vector<Node*>& new_nodes_on_domain,
27911 Vector<Vector<Vector<std::map<unsigned, Node*>>>>&
27912 other_proc_shd_bnd_node_pt,
27913 unsigned& iproc,
27914 unsigned& node_index,
27915 FiniteElement* const& new_el_pt,
27916 Vector<Vector<Vector<unsigned>>>& global_node_names,
27917 std::map<Vector<unsigned>, unsigned>& node_name_to_global_index,
27918 Vector<Node*>& global_shared_node_pt)
27919 {
27920 // Get the number of processors
27921 const unsigned nproc = this->communicator_pt()->nproc();
27922 // Get the rank of the current processor
27923 const unsigned my_rank = this->communicator_pt()->my_rank();
27924
27925 // The first entry indicates the number of values at this new Node
27926 //(which may be different across the same element e.g. Lagrange multipliers)
27927#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27929 << " Number of values of external halo node "
27931 << std::endl;
27932#endif
27934
27935 // Null TimeStepper for now
27936 TimeStepper* time_stepper_pt = this->Time_stepper_pt;
27937 // Default number of previous values to 1
27938 unsigned n_prev = time_stepper_pt->ntstorage();
27939
27940 // ------------------------------------------------------
27941 // Check if the node is on an original boundary
27942#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27944 << " Is the node on an original boundary "
27946 << std::endl;
27947#endif
27948
27949 // Flag to indicate if the node is on original boundaries
27950 const unsigned node_on_original_boundaries =
27952
27953 // Store the original boundaries where the node is on
27954 Vector<unsigned> original_boundaries_node_is_on;
27955 // Store the zeta coordinates of the node on the original boundaries
27956 Vector<double> zeta_coordinates;
27957 // Store the number of original boundaries the node is on
27958 unsigned n_original_boundaries_node_is_on = 0;
27959
27960 if (node_on_original_boundaries == 2)
27961 {
27962 // How many original boundaries does the node live on?
27963#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27965 << " Number of boundaries the node is on: "
27967 << std::endl;
27968#endif
27969 n_original_boundaries_node_is_on =
27971
27972 // Resize the containers
27973 original_boundaries_node_is_on.resize(n_original_boundaries_node_is_on);
27974 zeta_coordinates.resize(n_original_boundaries_node_is_on);
27975
27976 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
27977 {
27978 // Boundary number
27979#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
27981 << " Node is on boundary "
27983 << std::endl;
27984#endif
27985 original_boundaries_node_is_on[i] =
27987 zeta_coordinates[i] =
27989 }
27990
27991 } // if (node_on_original_boundaries==2)
27992#ifdef PARANOID
27993 else
27994 {
27995 if (node_on_original_boundaries != 0)
27996 {
27997 std::ostringstream error_message;
27998 error_message
27999 << "The current node is not on an original boundary, this should\n"
28000 << "be indicated by a zero flag. However, the read value for\n"
28001 << "that flag is (" << node_on_original_boundaries << ").\n\n";
28002 throw OomphLibError(
28003 error_message.str(),
28004 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28005 OOMPH_EXCEPTION_LOCATION);
28006 } // if (node_on_original_boundaries != 0)
28007 }
28008#endif
28009
28010 // --------------------------------------------------------------
28011 // Check if the node was on a shared boundary with the iproc
28012 // processor
28013#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28015 << " Is node on shared boundary? "
28017 << std::endl;
28018#endif
28019 const unsigned is_node_on_shared_boundary =
28021 if (is_node_on_shared_boundary == 1)
28022 {
28023 // How many shared boundaries does the node live on?
28024#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28026 << " Number of boundaries the node is on: "
28028 << std::endl;
28029#endif
28030 const unsigned n_shd_bnd_node_is_on =
28032 Vector<unsigned> shd_bnds_node_is_on(n_shd_bnd_node_is_on);
28033 for (unsigned i = 0; i < n_shd_bnd_node_is_on; i++)
28034 {
28035 // Shared boundary number
28036#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28038 << " Node is on boundary "
28040 << std::endl;
28041#endif
28042 shd_bnds_node_is_on[i] =
28044 }
28045
28046 // Get the index of the node on the shared boundary
28047#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28049 << " Index of node on boundary "
28051 << std::endl;
28052#endif
28053 // Get the node index of the node on the shared boundary
28054 unsigned node_index_on_shared_boundary =
28056
28057 // Get the pointer to the node with the received info.
28058 new_nod_pt = this->sorted_shared_boundary_node_pt(
28059 shd_bnds_node_is_on[0], node_index_on_shared_boundary);
28060
28061 } // if (is_node_on_shared_boundary == 1)
28062#ifdef PARANOID
28063 else
28064 {
28065 if (is_node_on_shared_boundary != 0)
28066 {
28067 std::ostringstream error_message;
28068 error_message
28069 << "The current node is not on a shared boundary, this should\n"
28070 << "be indicated by a zero flag. However, the read value for\n"
28071 << "that flag is (" << is_node_on_shared_boundary << ").\n\n";
28072 throw OomphLibError(
28073 error_message.str(),
28074 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28075 OOMPH_EXCEPTION_LOCATION);
28076 } // if (node_on_shared_boundary != 0)
28077 }
28078#endif
28079
28080 // ------------------------------------------------------------
28081 // Is the node on a shared boundary with other processor?
28082#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28084 << " Is the node on shared boundaries with other processors "
28086 << std::endl;
28087#endif
28088
28089 // Is the node in shared boundaries no associated with the
28090 // receiver processor
28091 const unsigned is_the_node_in_shared_boundaries_with_other_processors =
28093
28094 // The containers where to store the info.
28095 Vector<unsigned> other_processor_1;
28096 Vector<unsigned> other_processor_2;
28097 Vector<unsigned> other_shared_boundaries;
28098 Vector<unsigned> other_indexes;
28099
28100 // How many shared bounaries with other processors the node lives on
28101 unsigned n_shd_bnd_with_other_procs_have_node = 0;
28102
28103 // Is the node on shared boundaries with other processors
28104 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28105 {
28106#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28108 << " In how many shared boundaries with other "
28109 << "processors is the node "
28111 << std::endl;
28112#endif
28113
28114 // How many nodes on other shared boundaries were found
28115 n_shd_bnd_with_other_procs_have_node =
28117
28118 // Resize the containers
28119 other_processor_1.resize(n_shd_bnd_with_other_procs_have_node);
28120 other_processor_2.resize(n_shd_bnd_with_other_procs_have_node);
28121 other_shared_boundaries.resize(n_shd_bnd_with_other_procs_have_node);
28122 other_indexes.resize(n_shd_bnd_with_other_procs_have_node);
28123
28124 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28125 {
28126#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28128 << " Processor where the other shared boundary"
28129 << "has the node"
28131 << std::endl;
28132#endif
28133 // Read the other processor 1
28134 other_processor_1[i] =
28136
28137#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28139 << " Processor where the other shared boundary"
28140 << "has the node"
28142 << std::endl;
28143#endif
28144 // Read the other processor 2
28145 other_processor_2[i] =
28147
28148#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28150 << " Other shared boundary id where the node is on: "
28152 << std::endl;
28153#endif
28154
28155 // Read the other shared boundary id
28156 other_shared_boundaries[i] =
28158
28159#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28161 << " Node index on the other shared boundary "
28163 << std::endl;
28164#endif
28165
28166 // Read the node index on the other shared boundary
28167 other_indexes[i] =
28169
28170 } // for (i < n_shd_bnd_with_other_procs_have_node)
28171
28172 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28173#ifdef PARANOID
28174 else
28175 {
28176 if (is_the_node_in_shared_boundaries_with_other_processors != 0)
28177 {
28178 std::ostringstream error_message;
28179 error_message
28180 << "The current node is not on a shared boundary with\n"
28181 << "other processors, this should be indicated by a zero flag.\n"
28182 << "However, the read value for that flag is ("
28183 << is_the_node_in_shared_boundaries_with_other_processors << ").\n\n";
28184 throw OomphLibError(
28185 error_message.str(),
28186 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28187 OOMPH_EXCEPTION_LOCATION);
28188 }
28189 }
28190#endif
28191
28192 // ------------------------------------------------------------
28193 // Receive the info. to check if the node is on a haloed element
28194 // with any processor
28195
28196 // Store the halo element number with jproc where the node was found
28197 Vector<Vector<unsigned>> halo_element_number(nproc);
28198 // Store the node number on the halo element where the node was found
28199 Vector<Vector<unsigned>> halo_node_number_in_halo_element(nproc);
28200
28201 // Loop over the processors
28202 for (unsigned jproc = 0; jproc < nproc; jproc++)
28203 {
28204#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28206 << " The node is on "
28208 << " halo elements with " << jproc << " processor"
28209 << std::endl;
28210#endif
28211 // Get the number of halo elements with jproc processor where the
28212 // node was found
28213 const unsigned n_jproc_halo_ele_node_is_on =
28215
28216 // Resize the containers
28217 halo_element_number[jproc].resize(n_jproc_halo_ele_node_is_on);
28218 halo_node_number_in_halo_element[jproc].resize(
28219 n_jproc_halo_ele_node_is_on);
28220
28221 // Read halo elements indexes (which are indexes of the halo
28222 // elements of the sender processor (iproc) with other processors
28223 // (included my_rank)
28224 for (unsigned i = 0; i < n_jproc_halo_ele_node_is_on; i++)
28225 {
28226 // Get the halo element index in the jproc processor
28227#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28229 << " The halo element index where the node is on "
28231 << std::endl;
28232#endif
28233 // Get the node index on the halo element
28234 const unsigned halo_ele_index =
28236#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28238 << " The node index on the halo element where the node "
28239 << "is on "
28241 << std::endl;
28242#endif
28243 const unsigned node_index_on_halo_ele =
28245
28246 // Store the halo element number
28247 halo_element_number[jproc][i] = halo_ele_index;
28248 // Store the index of on the haloed element
28249 halo_node_number_in_halo_element[jproc][i] = node_index_on_halo_ele;
28250
28251 } // for (i < n_jproc_halo_ele_node_is_on)
28252
28253 } // for (jproc < nproc)
28254
28255 // Store the node pointers obtained from the indicated halo elements
28256 // (use a set to check for the case when the node pointer is
28257 // different)
28258 std::set<Node*> set_haloed_node_pt;
28259
28260 // Store the node pointer obtained from the haloed elements
28261 Node* haloed_node_pt = 0;
28262
28263 // Flag to indicate if it is on a haloed element of the current
28264 // processor with the iproc processor. If this flag is true then
28265 // there is no need to read the info. to create the node, only copy
28266 // the node from the indicated haloed element-node
28267 bool on_haloed_element_with_iproc_processor = false;
28268 if (halo_element_number[my_rank].size() > 0)
28269 {
28270 // The node is part of the haloed element in the current processor
28271 // (my_rank) with the receiver processor
28272 on_haloed_element_with_iproc_processor = true;
28273
28274 // Get the number of haloed elements in the current processor
28275 const unsigned n_haloed_indexes = halo_element_number[my_rank].size();
28276 // Loop over the different haloed indexes, and get the nodes
28277 // instances from all the indicated haloed elements (all of them
28278 // should be the same)
28279 for (unsigned i = 0; i < n_haloed_indexes; i++)
28280 {
28281 // Get the haloed element numbers where the node is on
28282 const unsigned haloed_index = halo_element_number[my_rank][i];
28283 // Get the node index on the haloed element
28284 const unsigned haloed_node_index =
28285 halo_node_number_in_halo_element[my_rank][i];
28286
28287 // Get the haloed element (with iproc)
28288 FiniteElement* tmp_haloed_ele_pt = f_haloed_ele_pt[iproc][haloed_index];
28289 // Get the node on the indicated node number
28290 Node* tmp_haloed_node_pt =
28291 tmp_haloed_ele_pt->node_pt(haloed_node_index);
28292
28293 // Set the pointer for the obtained haloed node
28294 haloed_node_pt = tmp_haloed_node_pt;
28295
28296 // Add the node to the set of node pointers
28297 set_haloed_node_pt.insert(tmp_haloed_node_pt);
28298
28299#ifdef PARANOID
28300 if (set_haloed_node_pt.size() > 1)
28301 {
28302 std::ostringstream error_message;
28303 error_message
28304 << "When adding the " << haloed_node_index << " node of the "
28305 << haloed_index << "-th haloed element\n"
28306 << "in the currrent processor with the " << iproc << " processor"
28307 << "it was found that\nthe node pointer is different from the other"
28308 << "instances of the node.\nIt means we have a repeated node."
28309 << "This are the node coordinates of the previous node instances\n"
28310 << "The last entry is for the just added node with a different "
28311 "node\n"
28312 << "pointer\n";
28313 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28314 it != set_haloed_node_pt.end();
28315 it++)
28316 {
28317 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28318 << ")\n";
28319 }
28320 error_message << "\n";
28321 throw OomphLibError(
28322 error_message.str(),
28323 "RefineableTriangleMesh::construct_new_node_load_balance_helper()",
28324 OOMPH_EXCEPTION_LOCATION);
28325 }
28326#endif
28327
28328 } // for (i < n_haloed_indexes)
28329
28330 } // if (halo_element_number[iproc].size() > 0)
28331
28332 // Flag to indicate if the node has been found on a haloed element
28333 // of other processor with the iproc processor
28334 bool found_on_haloed_element_with_other_processor = false;
28335 // Loop over the processors (only until the iproc since no info. of
28336 // higher processors has been received)
28337 for (unsigned jproc = 0; jproc < iproc; jproc++)
28338 {
28339 // Is the node on a halo element with the jproc processor
28340 if (halo_element_number[jproc].size() > 0)
28341 {
28342 // Get the number of halo elements with the jproc processor
28343 const unsigned n_halo_indexes = halo_element_number[jproc].size();
28344 // Loop over the different halo indexes, and get the nodes
28345 // instances from all the indicated halo elements (all of them
28346 // should be the same)
28347 for (unsigned i = 0; i < n_halo_indexes; i++)
28348 {
28349 // Get the haloed element numbers where the node is on
28350 const unsigned haloed_index = halo_element_number[jproc][i];
28351 // Get the node index on the haloed element
28352 const unsigned haloed_node_index =
28353 halo_node_number_in_halo_element[jproc][i];
28354
28355 // Have we received the indicated element? (Get the haloed
28356 // element on jproc with the iproc processor)
28357 std::map<unsigned, FiniteElement*>::iterator it_map =
28358 received_old_haloed_element_pt[jproc][iproc].find(haloed_index);
28359 // Have we received the indicated element?
28360 if (it_map != received_old_haloed_element_pt[jproc][iproc].end())
28361 {
28362 // Set the flag of found element in other processors haloed
28363 // element, in this case in haloed elements of processor
28364 // jproc wiht iproc processor
28365 found_on_haloed_element_with_other_processor = true;
28366
28367 // Get the element
28368 FiniteElement* tmp_haloed_ele_pt = (*it_map).second;
28369 // Get the node on the indicated node number
28370 Node* tmp_haloed_node_pt =
28371 tmp_haloed_ele_pt->node_pt(haloed_node_index);
28372
28373 // Set the pointer for the obtained haloed node
28374 haloed_node_pt = tmp_haloed_node_pt;
28375
28376 // Add the node to the set of node pointers
28377 set_haloed_node_pt.insert(tmp_haloed_node_pt);
28378
28379#ifdef PARANOID
28380 if (set_haloed_node_pt.size() > 1)
28381 {
28382 std::ostringstream error_message;
28383 error_message
28384 << "When adding the " << haloed_node_index << " node of the "
28385 << haloed_index << "-th haloed element "
28386 << "of the " << jproc << " processor\nwith the " << iproc
28387 << " processor, it was found that\n"
28388 << "the node pointer is different from the other\n"
28389 << "instances of the node.\nThis means we have a repeated "
28390 "node.\n"
28391 << "These are the node coordinates of the previous node "
28392 << "instances\n"
28393 << "The last entry is for the just added node with a "
28394 "different\n"
28395 << "node pointer\n";
28396 for (std::set<Node*>::iterator it = set_haloed_node_pt.begin();
28397 it != set_haloed_node_pt.end();
28398 it++)
28399 {
28400 error_message << "Node: (" << (*it)->x(0) << ", " << (*it)->x(1)
28401 << ")\n";
28402 }
28403 error_message << "\n";
28404 throw OomphLibError(error_message.str(),
28405 "RefineableTriangleMesh::construct_new_node_"
28406 "load_balance_helper()",
28407 OOMPH_EXCEPTION_LOCATION);
28408 }
28409#endif
28410
28411 } // if (it_map != received_old_haloed_element_pt[jproc][iproc].end())
28412 // Have we received the element?
28413
28414 } // for (i < n_haloed_indexes)
28415
28416 } // if (halo_element_number[iproc].size() > 0)
28417
28418 } // for (jproc < nproc)
28419
28420 // If the node was found in the haloed elements of the current
28421 // processor with the iproc processor, or in the haloed elements of
28422 // any other processor with the iproc processor then copy the node
28423 // pointer (no problem if we overwrite the node info. it should be
28424 // the same node pointer)
28425 if (on_haloed_element_with_iproc_processor ||
28426 found_on_haloed_element_with_other_processor)
28427 {
28428 // Set the node pointer
28429 new_nod_pt = haloed_node_pt;
28430 }
28431
28432 // Now we have all the info. to decide if the node should be created
28433 // or not
28434
28435 // First check if the node is a shared boundary node, or if it has
28436 // been found on haloed elements
28437 if (is_node_on_shared_boundary == 1 ||
28438 (on_haloed_element_with_iproc_processor))
28439 {
28440 // We already have the node, we do not need to create it
28441
28442 // Only check if we need to add boundary info. to the node
28443 if (node_on_original_boundaries == 2)
28444 {
28445 // The node is a boundary node, add the boundary info. before
28446 // adding it to the domain
28447
28448 // Associate the node to the given boundaries
28449 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28450 {
28451 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28452 // Establish the boundary coordinates for the node
28453 Vector<double> zeta(1);
28454 zeta[0] = zeta_coordinates[i];
28455 new_nod_pt->set_coordinates_on_boundary(
28456 original_boundaries_node_is_on[i], zeta);
28457 }
28458
28459 } // if (node_on_original_boundaries==2)
28460
28461 // Add the node to the domain
28462 new_nodes_on_domain.push_back(new_nod_pt);
28463
28464 // Add the node to the element
28465 new_el_pt->node_pt(node_index) = new_nod_pt;
28466
28467 } // if (is_node_on_shared_boundary == 1)
28468
28469 // Now check if the node is on a shared boundary with another
28470 // processor, if that is the case try to find the node that may have
28471 // been already sent by the other processors
28472
28473 // This flags indicates if the node was found, and then decide if it
28474 // is required to create the node
28475 bool found_node_in_other_shared_boundaries = false;
28476 // Flag to indicate whether the node should be created as a boundary
28477 // node or not. If the node lies on a shared boundary with other
28478 // processor the we create it as a boundary node. The processor from
28479 // which we are receiving info. (iproc) may not know that the node
28480 // lies on an original boundary. If the node lies on an original
28481 // boundary then its info. will be sent by another processor, then
28482 // we can set its boundary info. since the node was constructed as a
28483 // boundary node
28484 bool build_node_as_boundary_node = false;
28485
28486 if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28487 {
28488 // Build the node as a boundary node
28489 build_node_as_boundary_node = true;
28490
28491 // Try to get the node pointer in case that the node has been
28492 // already sent by the other processors
28493
28494 // Get the number of initial shared boundaries to correct the
28495 // index of the shared boundary
28496 const unsigned initial_shd_bnd_id = this->initial_shared_boundary_id();
28497
28498 // Add the found nodes in the container, should be the same but
28499 // better check
28500 Vector<Node*> found_node_pt;
28501
28502 // Now try to find the node in any of the other shared boundaries
28503 for (unsigned i = 0; i < n_shd_bnd_with_other_procs_have_node; i++)
28504 {
28505 unsigned oproc1 = other_processor_1[i];
28506 unsigned oproc2 = other_processor_2[i];
28507
28508 // Check that we always check with the lower processors number
28509 // first
28510 if (oproc1 > oproc2)
28511 {
28512 oproc2 = oproc1;
28513 oproc1 = other_processor_2[i];
28514 } // if (oproc1 > oproc2)
28515
28516 // Re-compute the shared boundary id between the other
28517 // processors
28518 const unsigned shd_bnd_id =
28519 other_shared_boundaries[i] - initial_shd_bnd_id;
28520 // Read the index
28521 const unsigned index = other_indexes[i];
28522
28523 // Check if there are nodes received from the other processor
28524 // and with the given shared boundary
28525 const unsigned n_nodes_on_other_processor =
28526 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].size();
28527
28528 if (n_nodes_on_other_processor > 0)
28529 {
28530 // Check if we can find the index of the node in that other
28531 // processor and shared boundary id
28532 std::map<unsigned, Node*>::iterator it =
28533 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].find(index);
28534
28535 // If the index exist then get the node pointer
28536 if (it !=
28537 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
28538 {
28539 // Mark the node as found
28540 found_node_in_other_shared_boundaries = true;
28541 // Get the node pointer
28542 Node* tmp_node_pt =
28543 other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id][index];
28544 found_node_pt.push_back(tmp_node_pt);
28545 } // if (it!=
28546 // other_proc_shd_bnd_node_pt[oproc1][oproc2][shd_bnd_id].end())
28547
28548 } // if (n_nodes_on_other_processor > 0)
28549
28550 } // for (i < n_shd_bnd_with_other_procs_have_node)
28551
28552 // If the node was found, then all their instances should be the
28553 // same but better check
28554 if (found_node_in_other_shared_boundaries)
28555 {
28556#ifdef PARANOID
28557 const unsigned ntimes_node_found = found_node_pt.size();
28558 for (unsigned j = 1; j < ntimes_node_found; j++)
28559 {
28560 if (found_node_pt[j - 1] != found_node_pt[j])
28561 {
28562 std::ostringstream error_message;
28563 error_message
28564 << "The instances of the node that was found to be on a\n"
28565 << "shared boundary with other processors are not the same,\n"
28566 << "the coordinates for the nodes are these:\n"
28567 << "(" << found_node_pt[j - 1]->x(0) << ", "
28568 << found_node_pt[j - 1]->x(1) << ")\n"
28569 << "(" << found_node_pt[j]->x(0) << ", " << found_node_pt[j]->x(1)
28570 << ")\n"
28571 << "Not be surprised if they are the same since the node is\n"
28572 << "repeated!!!\n";
28573 throw OomphLibError(
28574 error_message.str(),
28575 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28576 OOMPH_EXCEPTION_LOCATION);
28577
28578 } // if (found_node_pt[j-1] != found_node_pt[j])
28579
28580 } // for (j < ntimes_node_found)
28581#endif
28582
28583 // Check if the node is a shared boundary node from the current
28584 // processor and the iproc processor, if that is the case, and
28585 // the node is also on a shared boundary with other processor,
28586 // then the pointer should be the same!!!
28587 if (is_node_on_shared_boundary == 1)
28588 {
28589 // The pointer to the node is already assigned, it was
28590 // assigned when thenode was found to be on a shared boundary
28591 // with the iproc processor
28592 if (found_node_pt[0] != new_nod_pt)
28593 {
28594 std::ostringstream error_message;
28595 error_message
28596 << "The pointer of the node that was found to be on a\n"
28597 << "shared boundary with other processor(s) and the pointer\n"
28598 << "of the node on shared boundary with the receiver\n"
28599 << "processor (iproc) are not the same. This means we have a\n"
28600 << "repeated node)\n"
28601 << "The coordinates for the nodes are:\n"
28602 << "(" << found_node_pt[0]->x(0) << ", " << found_node_pt[0]->x(1)
28603 << ")\n"
28604 << "(" << new_nod_pt->x(0) << ", " << new_nod_pt->x(1) << ")\n"
28605 << "Not to be surprised if they are the same since the node is\n"
28606 << "repeated!!!\n";
28607 throw OomphLibError(
28608 error_message.str(),
28609 "RefineableTriangleMesh::construct_new_halo_node_helper()",
28610 OOMPH_EXCEPTION_LOCATION);
28611 } // if (found_node_pt[0] != new_nod_pt)
28612
28613 } // if (is_node_on_shared_boundary == 1)
28614 else
28615 {
28616 // Take the first instance of the node in case that it was
28617 // found and is not on a shared boundary with the iproc
28618 // processor
28619 new_nod_pt = found_node_pt[0];
28620 }
28621
28622 } // if (found_node_in_other_shared_boundaries)
28623
28624 } // if (is_the_node_in_shared_boundaries_with_other_processors == 4)
28625
28626 // -----------------------------------------------------------------
28627 // Create the node or read the received info if the node is not on a
28628 // shared boundary with the iproc processor and if the node is not
28629 // part of the haloed elements with the iproc processor in the
28630 // current processors
28631 if (is_node_on_shared_boundary != 1 &&
28632 !on_haloed_element_with_iproc_processor)
28633 {
28634 // If the node is on a shared boundary with other processor we
28635 // need to read all the info. since the processor that sent the
28636 // info. did not know that the node is part of another shared
28637 // boundary
28638
28639 // If the node is not a shared boundary (with any processor), or
28640 // if this is the first time that the info. of the node is
28641 // received from any of the processors with which is has a shared
28642 // boundary, then we create the node
28643
28644 // Is the node a boundary node or should it be build as a boundary
28645 // node because it is on a shared boundary with other processors
28646 if (node_on_original_boundaries == 2 || build_node_as_boundary_node)
28647 {
28648 // Check if necessary to create the node, or if it has been
28649 // already found in shared boundaries with other processors or
28650 // in the haloed elements with of other processors with the
28651 // iproc processor
28652 if (!found_node_in_other_shared_boundaries ||
28653 !found_on_haloed_element_with_other_processor)
28654 {
28655 // Construct a boundary node
28656 if (time_stepper_pt != 0)
28657 {
28658 new_nod_pt =
28659 new_el_pt->construct_boundary_node(node_index, time_stepper_pt);
28660 }
28661 else
28662 {
28663 new_nod_pt = new_el_pt->construct_boundary_node(node_index);
28664 }
28665
28666 } // if (!found_node_in_other_shared_boundaries ||
28667 // !found_on_haloed_element_with_other_processor)
28668 else
28669 {
28670 // If the node was found then assign the node to the element
28671 new_el_pt->node_pt(node_index) = new_nod_pt;
28672 } // else if (!found_node_in_other_shared_boundaries ||
28673 // !found_on_haloed_element_with_other_processor)
28674
28675 // Associate the node to the given boundaries
28676 for (unsigned i = 0; i < n_original_boundaries_node_is_on; i++)
28677 {
28678 add_boundary_node(original_boundaries_node_is_on[i], new_nod_pt);
28679 // Establish the boundary coordinates for the node
28680 Vector<double> zeta(1);
28681 zeta[0] = zeta_coordinates[i];
28682 new_nod_pt->set_coordinates_on_boundary(
28683 original_boundaries_node_is_on[i], zeta);
28684 }
28685
28686 } // if (node is on an original boundary)
28687 else
28688 {
28689 // Check if necessary to create the node, or if it has been
28690 // already found in shared boundaries with other processors or
28691 // in the haloed elements with of other processors with the
28692 // iproc processor
28693 if (!found_node_in_other_shared_boundaries ||
28694 !found_on_haloed_element_with_other_processor)
28695 {
28696 // Construct an ordinary (non-boundary) node
28697 if (time_stepper_pt != 0)
28698 {
28699 new_nod_pt = new_el_pt->construct_node(node_index, time_stepper_pt);
28700 }
28701 else
28702 {
28703 new_nod_pt = new_el_pt->construct_node(node_index);
28704 }
28705 } // if (!found_node_in_other_shared_boundaries ||
28706 // !found_on_haloed_element_with_other_processor)
28707 else
28708 {
28709 // If the node was found then assign the node to the element
28710 new_el_pt->node_pt(node_index) = new_nod_pt;
28711 } // else // if (!found_node_in_other_shared_boundaries ||
28712 // !found_on_haloed_element_with_other_processor)
28713
28714 } // else (the node is not a boundary node)
28715
28716 // ... and gather all its information
28717
28718 // If the node was found or not in other shared boundaries, this
28719 // is the first time the node is received from this processor
28720 // (iproc), therefore it is added to the vector of nodes received
28721 // from this processor (iproc)
28722 new_nodes_on_domain.push_back(new_nod_pt);
28723
28724 // Check if necessary to state all the info. to the node if it has
28725 // been already found in shared boundaries with other processors
28726 // or in the haloed elements with of other processors with the
28727 // iproc processor
28728 if (!found_node_in_other_shared_boundaries ||
28729 !found_on_haloed_element_with_other_processor)
28730 {
28731 // Add the node to the general node storage
28732 this->add_node_pt(new_nod_pt);
28733 } // if (!found_node_in_other_shared_boundaries ||
28734 // !found_on_haloed_element_with_other_processor)
28735
28736 // Is the new constructed node Algebraic?
28737 AlgebraicNode* new_alg_nod_pt = dynamic_cast<AlgebraicNode*>(new_nod_pt);
28738
28739 // If it is algebraic, its node update functions will
28740 // not yet have been set up properly
28741 if (new_alg_nod_pt != 0)
28742 {
28743 // The AlgebraicMesh is the external mesh
28744 AlgebraicMesh* alg_mesh_pt = dynamic_cast<AlgebraicMesh*>(this);
28745
28746 /// The first entry of All_alg_nodal_info contains
28747 /// the default node update id
28748 /// e.g. for the quarter circle there are
28749 /// "Upper_left_box", "Lower right box" etc...
28750#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28752 << " Alg node update id "
28754 << std::endl;
28755#endif
28756
28757 unsigned update_id =
28759
28760 Vector<double> ref_value;
28761
28762 // The size of this vector is in the next entry
28763 // of All_alg_nodal_info
28764#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28766 << " Alg node # of ref values "
28768 << std::endl;
28769#endif
28770 unsigned n_ref_val =
28772
28773 // The reference values themselves are in
28774 // All_alg_ref_value
28775 ref_value.resize(n_ref_val);
28776 for (unsigned i_ref = 0; i_ref < n_ref_val; i_ref++)
28777 {
28778 ref_value[i_ref] =
28780 }
28781
28782 Vector<GeomObject*> geom_object_pt;
28783 /// again we need the size of this vector as it varies
28784 /// between meshes; we also need some indication
28785 /// as to which geometric object should be used...
28786
28787 // The size of this vector is in the next entry
28788 // of All_alg_nodal_info
28789#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28791 << " Alg node # of geom objects "
28793 << std::endl;
28794#endif
28795 unsigned n_geom_obj =
28797
28798 // The remaining indices are in the rest of
28799 // All_alg_nodal_info
28800 geom_object_pt.resize(n_geom_obj);
28801 for (unsigned i_geom = 0; i_geom < n_geom_obj; i_geom++)
28802 {
28803#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28805 << " Alg node: geom object index "
28807 << std::endl;
28808#endif
28809 unsigned geom_index =
28811 // This index indicates which of the AlgebraicMesh's
28812 // stored geometric objects should be used
28813 // (0 is a null pointer; everything else should have
28814 // been filled in by the specific Mesh). If it
28815 // hasn't been filled in then the update_node_update
28816 // call should fix it
28817 geom_object_pt[i_geom] = alg_mesh_pt->geom_object_list_pt(geom_index);
28818 }
28819
28820 // Check if necessary to state all the info. to the node if it
28821 // has been already found in shared boundaries with other
28822 // processors or in the haloed elements with of other processors
28823 // with the iproc processor
28824 if (!found_node_in_other_shared_boundaries ||
28825 !found_on_haloed_element_with_other_processor)
28826 {
28827 /// For the received update_id, ref_value, geom_object
28828 /// call add_node_update_info
28829 new_alg_nod_pt->add_node_update_info(
28830 update_id, alg_mesh_pt, geom_object_pt, ref_value);
28831
28832 /// Now call update_node_update
28833 alg_mesh_pt->update_node_update(new_alg_nod_pt);
28834
28835 } // if (!found_node_in_other_shared_boundaries ||
28836 // !found_on_haloed_element_with_other_processor)
28837
28838 } // if (new_alg_nod_pt!=0)
28839
28840 // Check if necessary to state all the info. to the node if it has
28841 // been already found in shared boundaries with other processors
28842 // or in the haloed elements with of other processors with the
28843 // iproc processor
28844 if (!found_node_in_other_shared_boundaries ||
28845 !found_on_haloed_element_with_other_processor)
28846 {
28847 // Is the node a MacroElementNodeUpdateNode?
28848 MacroElementNodeUpdateNode* macro_nod_pt =
28849 dynamic_cast<MacroElementNodeUpdateNode*>(new_nod_pt);
28850
28851 if (macro_nod_pt != 0)
28852 {
28853 // Need to call set_node_update_info; this requires
28854 // a Vector<GeomObject*> (taken from the mesh)
28855 Vector<GeomObject*> geom_object_vector_pt;
28856
28857 // Access the required geom objects from the
28858 // MacroElementNodeUpdateMesh
28859 MacroElementNodeUpdateMesh* macro_mesh_pt =
28860 dynamic_cast<MacroElementNodeUpdateMesh*>(this);
28861 geom_object_vector_pt = macro_mesh_pt->geom_object_vector_pt();
28862
28863 // Get local coordinate of node in new element
28864 Vector<double> s_in_macro_node_update_element;
28865 new_el_pt->local_coordinate_of_node(node_index,
28866 s_in_macro_node_update_element);
28867
28868 // Set node update info for this node
28869 macro_nod_pt->set_node_update_info(
28870 new_el_pt, s_in_macro_node_update_element, geom_object_vector_pt);
28871 }
28872
28873 } // if (!found_node_in_other_shared_boundaries ||
28874 // !found_on_haloed_element_with_other_processor)
28875
28876 // If there are additional values, resize the node
28877 unsigned n_new_val = new_nod_pt->nvalue();
28878
28879 // Check if necessary to state all the info. to the node if it has
28880 // been already found in shared boundaries with other processors
28881 // or in the haloed elements with of other processors with the
28882 // iproc processor
28883 if (!found_node_in_other_shared_boundaries ||
28884 !found_on_haloed_element_with_other_processor)
28885 {
28886 if (n_val > n_new_val)
28887 {
28888 // If it has been necessary to resize then it may be becuse
28889 // the node is on a FSI boundary, if that is the case we need
28890 // to set a map for these external values
28891
28892 // Cast to a boundary node
28893 BoundaryNodeBase* bnod_pt =
28894 dynamic_cast<BoundaryNodeBase*>(new_nod_pt);
28895
28896 // Create storage, if it doesn't already exist, for the map
28897 // that will contain the position of the first entry of
28898 // this face element's additional values,
28900 {
28902 new std::map<unsigned, unsigned>;
28903 }
28904
28905 // Get pointer to the map
28906 std::map<unsigned, unsigned>* map_pt =
28908
28909 // The id of the face to which this node belong in the bulk
28910 // element
28911 const unsigned id_face = 0;
28912 // We only resize the node values Vector if we haven't done it yet
28913 std::map<unsigned, unsigned>::const_iterator p =
28914 map_pt->find(id_face);
28915
28916 // If this node hasn't been resized for current id
28917 if (p == map_pt->end())
28918 {
28919 // assign the face element id and the position of the
28920 // first entry to the boundary node
28921 (*map_pt)[id_face] = n_new_val;
28922
28923 // resize the node vector of values
28924 new_nod_pt->resize(n_val);
28925 }
28926
28927 } // if (n_val>n_new_val)
28928
28929 } // if (!found_node_in_other_shared_boundaries ||
28930 // !found_on_haloed_element_with_other_processor)
28931
28932 // Is the new node a SolidNode?
28933 SolidNode* solid_nod_pt = dynamic_cast<SolidNode*>(new_nod_pt);
28934 if (solid_nod_pt != 0)
28935 {
28936 unsigned n_solid_val = solid_nod_pt->variable_position_pt()->nvalue();
28937 for (unsigned i_val = 0; i_val < n_solid_val; i_val++)
28938 {
28939 for (unsigned t = 0; t < n_prev; t++)
28940 {
28941 double read_data =
28943
28944 // Check if necessary to state all the info. to the node if
28945 // it has been already found in shared boundaries with other
28946 // processors or in the haloed elements with of other
28947 // processors with the iproc processor
28948 if (!found_node_in_other_shared_boundaries ||
28949 !found_on_haloed_element_with_other_processor)
28950 {
28951 solid_nod_pt->variable_position_pt()->set_value(
28952 t, i_val, read_data);
28953 } // if (!found_node_in_other_shared_boundaries ||
28954 // !found_on_haloed_element_with_other_processor)
28955 }
28956 }
28957
28958#ifdef ANNOTATE_REFINEABLE_TRIANGLE_MESH_COMMUNICATION_LOAD_BALANCE
28960 << " Number of values solid node: "
28962 << std::endl;
28963#endif
28964 const unsigned nvalues_solid_node =
28966 Vector<double> values_solid_node(nvalues_solid_node);
28967 for (unsigned i = 0; i < nvalues_solid_node; i++)
28968 {
28969 values_solid_node[i] =
28971 }
28972
28973 // Check if necessary to state all the info. to the node if it
28974 // has been already found in shared boundaries with other
28975 // processors or in the haloed elements with of other processors
28976 // with the iproc processor
28977 if (!found_node_in_other_shared_boundaries ||
28978 !found_on_haloed_element_with_other_processor)
28979 {
28980 unsigned index = 0;
28981 solid_nod_pt->read_values_from_vector(values_solid_node, index);
28982 } // if (!found_node_in_other_shared_boundaries ||
28983 // !found_on_haloed_element_with_other_processor)
28984 }
28985
28986 // Get copied history values
28987 // unsigned n_val=new_nod_pt->nvalue();
28988 for (unsigned i_val = 0; i_val < n_val; i_val++)
28989 {
28990 for (unsigned t = 0; t < n_prev; t++)
28991 {
28992 double read_data =
28994
28995 // Check if necessary to state all the info. to the node if it
28996 // has been already found in shared boundaries with other
28997 // processors or in the haloed elements with of other
28998 // processors with the iproc processor
28999 if (!found_node_in_other_shared_boundaries ||
29000 !found_on_haloed_element_with_other_processor)
29001 {
29002 new_nod_pt->set_value(t, i_val, read_data);
29003 } // if (!found_node_in_other_shared_boundaries ||
29004 // !found_on_haloed_element_with_other_processor)
29005 }
29006 }
29007
29008 // Get copied history values for positions
29009 unsigned n_dim = new_nod_pt->ndim();
29010 for (unsigned idim = 0; idim < n_dim; idim++)
29011 {
29012 for (unsigned t = 0; t < n_prev; t++)
29013 {
29014 double read_data =
29016
29017 // Check if necessary to state all the info. to the node if it
29018 // has been already found in shared boundaries with other
29019 // processors or in the haloed elements with of other
29020 // processors with the iproc processor
29021 if (!found_node_in_other_shared_boundaries ||
29022 !found_on_haloed_element_with_other_processor)
29023 {
29024 // Copy to coordinate
29025 new_nod_pt->x(t, idim) = read_data;
29026 // DEBP(new_nod_pt->x(t,idim));
29027 } // if (!found_node_in_other_shared_boundaries ||
29028 // !found_on_haloed_element_with_other_processor)
29029 }
29030 }
29031
29032 } // if (is_node_on_shared_boundary != 1)
29033
29034 // If the node was not found in other shared boundaries (possibly
29035 // because it is the first time the node has been sent) then copy
29036 // the node to the shared boundaries where it should be, use the
29037 // special container for this cases
29038 if (n_shd_bnd_with_other_procs_have_node > 0 && // The node is on
29039 // shared
29040 // boundaries with
29041 // other processors
29042 !found_node_in_other_shared_boundaries) // The node has not
29043 // been previously
29044 // set as with
29045 // shared with
29046 // other processors
29047 // (first time)
29048 {
29049 // Update the node pointer in all the references of the node
29050 this->update_other_proc_shd_bnd_node_helper(new_nod_pt,
29051 other_proc_shd_bnd_node_pt,
29052 other_processor_1,
29053 other_processor_2,
29054 other_shared_boundaries,
29055 other_indexes,
29056 global_node_names,
29057 node_name_to_global_index,
29058 global_shared_node_pt);
29059
29060 } // if (!found_node_in_other_shared_boundaries)
29061 }
29062
29063#endif // #ifdef OOMPH_HAS_MPI
29064
29065 //======================================================================
29066 /// Get the nodes on the boundary (b), these are stored in the
29067 /// segment they belong (also used by the load balance method to
29068 /// re-set the number of segments per boundary after load balance has
29069 /// taken place)
29070 //======================================================================
29071 template<class ELEMENT>
29073 const unsigned& b, Vector<Vector<Node*>>& tmp_segment_nodes)
29074 {
29075 // Clear the data structure were to return the nodes
29076 tmp_segment_nodes.clear();
29077
29078 // Temporary storage for face elements
29079 Vector<FiniteElement*> face_el_pt;
29080
29081 // Temporary storage for number of elements adjacent to the boundary
29082 unsigned nel = 0;
29083
29084 // Temporary storage for elements adjacent to the boundary that have
29085 // a common edge (related with internal boundaries)
29086 unsigned n_repeated_ele = 0;
29087
29088 // Get the number of regions
29089 const unsigned n_regions = this->nregion();
29090
29091 // Temporary storage for already visited pair of nodes (edges)
29092 Vector<std::pair<Node*, Node*>> done_nodes_pt;
29093
29094 // Are there more than one region?
29095 if (n_regions > 1)
29096 {
29097 for (unsigned rr = 0; rr < n_regions; rr++)
29098 {
29099 const unsigned region_id =
29100 static_cast<unsigned>(this->Region_attribute[rr]);
29101
29102 // Loop over all elements on boundaries in region rr
29103 const unsigned nel_in_region =
29104 this->nboundary_element_in_region(b, region_id);
29105
29106 // Number of repeated element in region
29107 unsigned nel_repeated_in_region = 0;
29108
29109 // Only bother to do anything else, if there are elements
29110 // associated with the boundary and the current region
29111 if (nel_in_region > 0)
29112 {
29113 // Flag that activates when a repeated face element is found,
29114 // possibly because we are dealing with an internal boundary
29115 bool repeated = false;
29116
29117 // Loop over the bulk elements adjacent to boundary b
29118 for (unsigned e = 0; e < nel_in_region; e++)
29119 {
29120 // Get pointer to the bulk element that is adjacent to boundary b
29121 FiniteElement* bulk_elem_pt =
29122 this->boundary_element_in_region_pt(b, region_id, e);
29123
29124#ifdef OOMPH_HAS_MPI
29125 // In a distributed mesh only work with nonhalo elements
29126 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29127 {
29128 // Increase the number of repeated elements
29129 n_repeated_ele++;
29130 // Go for the next element
29131 continue;
29132 }
29133#endif
29134
29135 // Find the index of the face of element e along boundary b
29136 int face_index =
29137 this->face_index_at_boundary_in_region(b, region_id, e);
29138
29139 // Before adding the new element we need to be sure that the
29140 // edge that this element represents has not been already
29141 // added
29142 FiniteElement* tmp_ele_pt =
29143 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29144
29145 // Number of nodes in the face element
29146 const unsigned n_nodes = tmp_ele_pt->nnode();
29147
29148 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29149 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29150
29151 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29152 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
29153
29154 // Search for repeated nodes
29155 unsigned n_done_nodes = done_nodes_pt.size();
29156 for (unsigned l = 0; l < n_done_nodes; l++)
29157 {
29158 if (tmp_pair == done_nodes_pt[l] ||
29159 tmp_pair_inverse == done_nodes_pt[l])
29160 {
29161 nel_repeated_in_region++;
29162 repeated = true;
29163 break;
29164 }
29165
29166 } // for (l < n_done_nodes)
29167
29168 // Create new face element?
29169 if (!repeated)
29170 {
29171 // Add the pair of nodes (edge) to the node dones
29172 done_nodes_pt.push_back(tmp_pair);
29173 // Add the face element to the storage
29174 face_el_pt.push_back(tmp_ele_pt);
29175 }
29176 else
29177 {
29178 // Clean up
29179 delete tmp_ele_pt;
29180 tmp_ele_pt = 0;
29181 }
29182
29183 // Re-start
29184 repeated = false;
29185
29186 } // for (e < nel_in_region)
29187
29188 // Add on the number of elements in the boundary with the
29189 // current region
29190 nel += nel_in_region;
29191
29192 // Add on the number of repeated elements
29193 n_repeated_ele += nel_repeated_in_region;
29194
29195 } // if (nel_in_region > 0)
29196
29197 } // for (rr < n_regions)
29198
29199 } // if (n_regions > 1)
29200 // Otherwise it's just the normal boundary functions
29201 else
29202 {
29203 // Assign the number of boundary elements
29204 nel = this->nboundary_element(b);
29205
29206 // Only bother to do anything else, if there are elements
29207 if (nel > 0)
29208 {
29209 // Flag that activates when a repeated face element is found,
29210 // possibly because we are dealing with an internal boundary
29211 bool repeated = false;
29212
29213 // Loop over the bulk elements adjacent to boundary b
29214 for (unsigned e = 0; e < nel; e++)
29215 {
29216 // Get pointer to the bulk element that is adjacent to boundary b
29217 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
29218
29219#ifdef OOMPH_HAS_MPI
29220 // In a distributed mesh only work with nonhalo elements
29221 if (this->is_mesh_distributed() && bulk_elem_pt->is_halo())
29222 {
29223 // Increase the number of repeated elements
29224 n_repeated_ele++;
29225 // Go for the next element
29226 continue;
29227 }
29228#endif
29229
29230 // Find the index of the face of element e along boundary b
29231 int face_index = this->face_index_at_boundary(b, e);
29232
29233 // Before adding the new element we need to be sure that the
29234 // edge that this element represent has not been already added
29235 FiniteElement* tmp_ele_pt =
29236 new DummyFaceElement<ELEMENT>(bulk_elem_pt, face_index);
29237
29238 // Number of nodes in the face element
29239 const unsigned n_nodes = tmp_ele_pt->nnode();
29240
29241 std::pair<Node*, Node*> tmp_pair = std::make_pair(
29242 tmp_ele_pt->node_pt(0), tmp_ele_pt->node_pt(n_nodes - 1));
29243
29244 std::pair<Node*, Node*> tmp_pair_inverse = std::make_pair(
29245 tmp_ele_pt->node_pt(n_nodes - 1), tmp_ele_pt->node_pt(0));
29246
29247 // Search for repeated nodes
29248 unsigned n_done_nodes = done_nodes_pt.size();
29249 for (unsigned l = 0; l < n_done_nodes; l++)
29250 {
29251 if (tmp_pair == done_nodes_pt[l] ||
29252 tmp_pair_inverse == done_nodes_pt[l])
29253 {
29254 n_repeated_ele++;
29255 repeated = true;
29256 break;
29257 }
29258
29259 } // for (l < n_done_nodes)
29260
29261 // Create new face element
29262 if (!repeated)
29263 {
29264 // Add the pair of nodes (edge) to the node dones
29265 done_nodes_pt.push_back(tmp_pair);
29266 // Add the face element to the storage
29267 face_el_pt.push_back(tmp_ele_pt);
29268 }
29269 else
29270 {
29271 // Free the repeated bulk element!!
29272 delete tmp_ele_pt;
29273 tmp_ele_pt = 0;
29274 }
29275
29276 // Re-start
29277 repeated = false;
29278
29279 } // for (e < nel)
29280
29281 } // if (nel > 0)
29282
29283 } // else if (n_regions > 1)
29284
29285 // Substract the repeated elements
29286 nel -= n_repeated_ele;
29287
29288#ifdef PARANOID
29289 if (nel != face_el_pt.size())
29290 {
29291 std::ostringstream error_message;
29292 error_message
29293 << "The independet counting of face elements (" << nel << ") for "
29294 << "boundary (" << b << ") is different\n"
29295 << "from the real number of face elements in the container ("
29296 << face_el_pt.size() << ")\n";
29297 throw OomphLibError(
29298 error_message.str(),
29299 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29300 OOMPH_EXCEPTION_LOCATION);
29301 }
29302#endif
29303
29304 // Only bother to do anything else, if there are elements
29305 if (nel > 0)
29306 {
29307 // Assign the number of nonhalo face elements
29308 const unsigned nnon_halo_face_elements = nel;
29309
29310 // The vector of list to store the "segments" that compound the
29311 // boundary (segments may appear only in a distributed mesh)
29312 Vector<std::list<FiniteElement*>> segment_sorted_ele_pt;
29313
29314 // Number of already sorted face elements (only nonhalo face
29315 // elements for a distributed mesh)
29316 unsigned nsorted_face_elements = 0;
29317
29318 // Keep track of who's done (in a distributed mesh this apply to
29319 // nonhalo only)
29320 std::map<FiniteElement*, bool> done_ele;
29321
29322 // Keep track of which element is inverted (in distributed mesh
29323 // the elements may be inverted with respect to the segment they
29324 // belong)
29325 std::map<FiniteElement*, bool> is_inverted;
29326
29327 // Iterate until all possible segments have been created. In a non
29328 // distributed mesh there is only one segment which defines the
29329 // complete boundary
29330 while (nsorted_face_elements < nnon_halo_face_elements)
29331 {
29332 // The ordered list of face elements (in a distributed mesh a
29333 // collection of continuous face elements define a segment)
29334 std::list<FiniteElement*> sorted_el_pt;
29335
29336#ifdef PARANOID
29337 // Select an initial element for the segment
29338 bool found_initial_face_element = false;
29339#endif
29340
29341 FiniteElement* ele_face_pt = 0;
29342
29343 unsigned iface = 0;
29344#ifdef OOMPH_HAS_MPI
29345 if (this->is_mesh_distributed())
29346 {
29347 for (iface = 0; iface < nel; iface++)
29348 {
29349 ele_face_pt = face_el_pt[iface];
29350 // If not done then take it as initial face element
29351 if (!done_ele[ele_face_pt])
29352 {
29353#ifdef PARANOID
29354 // Set the flag to indicate the initial element was
29355 // found
29356 found_initial_face_element = true;
29357#endif
29358 // Increase the number of sorted face elements
29359 nsorted_face_elements++;
29360 // Set the index to the next face element
29361 iface++;
29362 // Add the face element in the container
29363 sorted_el_pt.push_back(ele_face_pt);
29364 // Mark as done
29365 done_ele[ele_face_pt] = true;
29366 break;
29367 } // if (!done_el[ele_face_pt])
29368 } // for (iface < nel)
29369 } // if (this->is_mesh_distributed())
29370 else
29371 {
29372#endif // #ifdef OOMPH_HAS_MPI
29373
29374 // When the mesh is not distributed just take the first
29375 // element and put it in the ordered list
29376 ele_face_pt = face_el_pt[0];
29377#ifdef PARANOID
29378 // Set the flag to indicate the initial element was found
29379 found_initial_face_element = true;
29380#endif
29381 // Increase the number of sorted face elements
29382 nsorted_face_elements++;
29383 // Set the index to the next face element
29384 iface = 1;
29385 // Add the face element in the container
29386 sorted_el_pt.push_back(ele_face_pt);
29387 // Mark as done
29388 done_ele[ele_face_pt] = true;
29389#ifdef OOMPH_HAS_MPI
29390 } // else if (this->is_mesh_distributed())
29391#endif
29392
29393#ifdef PARANOID
29394 if (!found_initial_face_element)
29395 {
29396 std::ostringstream error_message;
29397 error_message << "Could not find an initial face element for the "
29398 "current segment\n";
29399 throw OomphLibError(
29400 error_message.str(),
29401 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29402 OOMPH_EXCEPTION_LOCATION);
29403 }
29404#endif
29405
29406 // Number of nodes in the face element
29407 const unsigned nnod = ele_face_pt->nnode();
29408
29409 // Left and rightmost nodes (the left and right nodes of the
29410 // current face element)
29411 Node* left_node_pt = ele_face_pt->node_pt(0);
29412 Node* right_node_pt = ele_face_pt->node_pt(nnod - 1);
29413
29414 // Continue iterating if a new face element has been added to
29415 // the list
29416 bool face_element_added = false;
29417
29418 // While a new face element has been added to the set of sorted
29419 // face elements continue iterating
29420 do
29421 {
29422 // Start from the next face element since we have already
29423 // added the previous one as the initial face element (any
29424 // previous face element had to be added on previous
29425 // iterations)
29426 for (unsigned iiface = iface; iiface < nel; iiface++)
29427 {
29428 // Re-start flag
29429 face_element_added = false;
29430
29431 // Get the candidate element
29432 ele_face_pt = face_el_pt[iiface];
29433
29434 // Check that the candidate element has not been done and is
29435 // not a halo element
29436 if (!done_ele[ele_face_pt])
29437 {
29438 // Get the left and right nodes of the current element
29439 Node* local_left_node_pt = ele_face_pt->node_pt(0);
29440 Node* local_right_node_pt = ele_face_pt->node_pt(nnod - 1);
29441
29442 // New element fits at the left of segment and is not inverted
29443 if (left_node_pt == local_right_node_pt)
29444 {
29445 left_node_pt = local_left_node_pt;
29446 sorted_el_pt.push_front(ele_face_pt);
29447 is_inverted[ele_face_pt] = false;
29448 face_element_added = true;
29449 }
29450 // New element fits at the left of segment and is inverted
29451 else if (left_node_pt == local_left_node_pt)
29452 {
29453 left_node_pt = local_right_node_pt;
29454 sorted_el_pt.push_front(ele_face_pt);
29455 is_inverted[ele_face_pt] = true;
29456 face_element_added = true;
29457 }
29458 // New element fits on the right of segment and is not inverted
29459 else if (right_node_pt == local_left_node_pt)
29460 {
29461 right_node_pt = local_right_node_pt;
29462 sorted_el_pt.push_back(ele_face_pt);
29463 is_inverted[ele_face_pt] = false;
29464 face_element_added = true;
29465 }
29466 // New element fits on the right of segment and is inverted
29467 else if (right_node_pt == local_right_node_pt)
29468 {
29469 right_node_pt = local_left_node_pt;
29470 sorted_el_pt.push_back(ele_face_pt);
29471 is_inverted[ele_face_pt] = true;
29472 face_element_added = true;
29473 }
29474
29475 if (face_element_added)
29476 {
29477 // Mark the face element as done
29478 done_ele[ele_face_pt] = true;
29479 nsorted_face_elements++;
29480 break;
29481 }
29482
29483 } // if (!done_el[ele_face_pt])
29484
29485 } // for (iiface<nnon_halo_face_element)
29486
29487 } while (face_element_added &&
29488 (nsorted_face_elements < nnon_halo_face_elements));
29489
29490 // Store the created segment in the vector of segments
29491 segment_sorted_ele_pt.push_back(sorted_el_pt);
29492
29493 } // while(nsorted_face_elements < nnon_halo_face_elements);
29494
29495 // The number of boundary segments in this processor
29496 const unsigned nsegments = segment_sorted_ele_pt.size();
29497
29498#ifdef PARANOID
29499 if (nnon_halo_face_elements > 0 && nsegments == 0)
29500 {
29501 std::ostringstream error_message;
29502 error_message
29503 << "The number of segments is zero, but the number of nonhalo\n"
29504 << "elements is: (" << nnon_halo_face_elements << ")\n";
29505 throw OomphLibError(
29506 error_message.str(),
29507 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29508 OOMPH_EXCEPTION_LOCATION);
29509 } // if (nnon_halo_face_elements > 0 && nsegments == 0)
29510#endif
29511
29512 // Go through all the segments, visit each face element in order
29513 // and get the nodes based that represent the boundary segment
29514
29515 // Resize the container to store the nodes with the required
29516 // number of segments
29517 tmp_segment_nodes.resize(nsegments);
29518
29519 for (unsigned is = 0; is < nsegments; is++)
29520 {
29521#ifdef PARANOID
29522 if (segment_sorted_ele_pt[is].size() == 0)
29523 {
29524 std::ostringstream error_message;
29525 error_message << "The (" << is << ")-th segment has no elements\n";
29526 throw OomphLibError(
29527 error_message.str(),
29528 "RefineableTriangleMesh::get_boundary_segment_nodes_helper()",
29529 OOMPH_EXCEPTION_LOCATION);
29530 } // if (segment_sorted_ele_pt[is].size() == 0)
29531#endif
29532
29533 // Get access to the first element on the segment
29534 FiniteElement* first_ele_pt = segment_sorted_ele_pt[is].front();
29535
29536 // Number of nodes
29537 const unsigned nnod = first_ele_pt->nnode();
29538
29539 // Get the first node of the current segment
29540 Node* first_node_pt = first_ele_pt->node_pt(0);
29541 if (is_inverted[first_ele_pt])
29542 {
29543 first_node_pt = first_ele_pt->node_pt(nnod - 1);
29544 }
29545
29546 // Add the node to the corresponding segment
29547 tmp_segment_nodes[is].push_back(first_node_pt);
29548
29549 // Now loop over face elements in order to get the nodes
29550 for (std::list<FiniteElement*>::iterator it =
29551 segment_sorted_ele_pt[is].begin();
29552 it != segment_sorted_ele_pt[is].end();
29553 it++)
29554 {
29555 // Get element
29556 FiniteElement* ele_pt = *it;
29557
29558 // The last node pointer
29559 Node* last_node_pt = 0;
29560
29561 // Get the last node
29562 if (!is_inverted[ele_pt])
29563 {
29564 last_node_pt = ele_pt->node_pt(nnod - 1);
29565 }
29566 else
29567 {
29568 last_node_pt = ele_pt->node_pt(0);
29569 }
29570
29571 // Add the node to the corresponding segment
29572 tmp_segment_nodes[is].push_back(last_node_pt);
29573
29574 } // iterator over the elements in the segment
29575
29576 } // for (is < nsegments)
29577
29578 } // for (if (nel > 0))
29579
29580 // Free memory allocation
29581 for (unsigned e = 0; e < nel; e++)
29582 {
29583 delete face_el_pt[e];
29584 face_el_pt[e] = 0;
29585 } // for (e < nel)
29586 }
29587
29588 //======================================================================
29589 /// Adapt problem based on specified elemental error estimates
29590 /// This function implement serial and parallel mesh adaptation, the
29591 /// sections for parallel mesh adaptation are clearly identified by
29592 /// checking whether the mesh is distributed or not
29593 //======================================================================
29594 template<class ELEMENT>
29596 {
29597 double t_start_overall = TimingHelpers::timer();
29598
29599 // ==============================================================
29600 // BEGIN: Compute target areas
29601 // ==============================================================
29602
29603 // Get refinement targets
29604 Vector<double> target_area(elem_error.size());
29605 double min_angle = compute_area_target(elem_error, target_area);
29606
29607 // Post-process to allow only quantised target areas
29608 // in an attempt to more closely mimick the structured
29609 // case and limit the diffusion of small elements.
29610 bool quantised_areas = true;
29611 if (quantised_areas)
29612 {
29613 unsigned n = target_area.size();
29614 double total_area = 0;
29615 // If the mesh is distributed then we need to get the contribution
29616 // of all processors to compute the total areas
29617 // ------------------------------------------
29618 // DISTRIBUTED MESH: BEGIN
29619 // ------------------------------------------
29620#ifdef OOMPH_HAS_MPI
29621 if (this->is_mesh_distributed())
29622 {
29623 // When working in parallel we get the total area from the sum
29624 // of the the sub-areas of all the meshes
29625 double sub_area = 0.0;
29626
29627 // Only add the area of nonhalo elements
29628 for (unsigned e = 0; e < n; e++)
29629 {
29630 // Get the pointer to the element
29631 FiniteElement* ele_pt = this->finite_element_pt(e);
29632 if (!ele_pt->is_halo())
29633 {
29634 sub_area += ele_pt->size();
29635 }
29636 } // for (e<n)
29637
29638 // Get the communicator of the mesh
29639 OomphCommunicator* comm_pt = this->communicator_pt();
29640
29641 // Get the total area
29642 MPI_Allreduce(
29643 &sub_area, &total_area, 1, MPI_DOUBLE, MPI_SUM, comm_pt->mpi_comm());
29644 }
29645 else
29646 {
29647 for (unsigned e = 0; e < n; e++)
29648 {
29649 total_area += this->finite_element_pt(e)->size();
29650 }
29651 }
29652 // ------------------------------------------
29653 // DISTRIBUTED MESH: END
29654 // ------------------------------------------
29655#else // #ifdef OOMPH_HAS_MPI
29656 for (unsigned e = 0; e < n; e++)
29657 {
29658 total_area += this->finite_element_pt(e)->size();
29659 }
29660#endif // #ifdef OOMPH_HAS_MPI
29661
29662 for (unsigned e = 0; e < n; e++)
29663 {
29664 unsigned level =
29665 unsigned(ceil(log(target_area[e] / total_area) / log(1.0 / 3.0))) - 1;
29666 double new_target_area = total_area * pow(1.0 / 3.0, int(level));
29667 target_area[e] = new_target_area;
29668 }
29669 }
29670
29671 // std::ofstream tmp;
29672 // tmp.open((Global_string_for_annotation::
29673 // String[0]+"overall_target_areas"+
29674 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
29675
29676 // Get maximum target area
29677 unsigned n = target_area.size();
29678 double max_area = 0.0;
29679 double min_area = DBL_MAX;
29680 for (unsigned e = 0; e < n; e++)
29681 {
29682 if (target_area[e] > max_area) max_area = target_area[e];
29683 if (target_area[e] < min_area) min_area = target_area[e];
29684
29685 // tmp << (finite_element_pt(e)->node_pt(0)->x(0)+
29686 // finite_element_pt(e)->node_pt(1)->x(0)+
29687 // finite_element_pt(e)->node_pt(2)->x(0))/3.0 << " "
29688 // << (finite_element_pt(e)->node_pt(0)->x(1)+
29689 // finite_element_pt(e)->node_pt(1)->x(1)+
29690 // finite_element_pt(e)->node_pt(2)->x(1))/3.0 << " "
29691 // << target_area[e] << " "
29692 // << finite_element_pt(e)->size() << " "
29693 // << elem_error[e] << " " << std::endl;
29694 }
29695
29696 // tmp.close();
29697
29698 oomph_info << "Maximum target area: " << max_area << std::endl;
29699 oomph_info << "Minimum target area: " << min_area << std::endl;
29700 oomph_info << "Number of elements to be refined: " << this->Nrefined
29701 << std::endl;
29702 oomph_info << "Number of elements to be unrefined: " << this->Nunrefined
29703 << std::endl;
29704 oomph_info << "Min. angle: " << min_angle << std::endl;
29705
29706 double orig_max_area, orig_min_area;
29707 this->max_and_min_element_size(orig_max_area, orig_min_area);
29708 oomph_info << "Max./min. element size in original mesh: " << orig_max_area
29709 << " " << orig_min_area << std::endl;
29710
29711 // ==============================================================
29712 // END: Compute target areas
29713 // ==============================================================
29714
29715 // Check if boundaries need to be updated (regardless of
29716 // requirements of bulk error estimator) but don't do anything!
29717 bool check_only = true;
29718 bool outer_boundary_update_necessary = false;
29719 bool inner_boundary_update_necessary = false;
29720 bool inner_open_boundary_update_necessary = false;
29721
29722 // Get the number of outer boundaries and check if they require
29723 // update
29724 const unsigned nouter = this->Outer_boundary_pt.size();
29725
29726 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29727 {
29728 // loop over the outer boundaries
29729 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29730 {
29731 outer_boundary_update_necessary = this->update_polygon_using_face_mesh(
29732 this->Outer_boundary_pt[i_outer], check_only);
29733 // Break the loop if at least one needs updating
29734 if (outer_boundary_update_necessary) break;
29735 }
29736
29737 // Do not waste time if we already know that it is necessary an update
29738 // on the boundary representation
29739 if (!outer_boundary_update_necessary)
29740 {
29741 // Check if we need to generate a new 1D mesh representation of
29742 // the inner hole boundaries
29743 const unsigned nhole = this->Internal_polygon_pt.size();
29744 Vector<Vector<double>> internal_point_coord(nhole);
29745 inner_boundary_update_necessary =
29746 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord,
29747 check_only);
29748
29749 // If there was not necessary a change even on the internal closed
29750 // curve then finally check for the open curves as well
29751 if (!inner_boundary_update_necessary)
29752 {
29753 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29754 // loop over the open polylines
29755 for (unsigned i = 0; i < n_open_polyline; i++)
29756 {
29757 inner_open_boundary_update_necessary =
29758 this->update_open_curve_using_face_mesh(
29759 this->Internal_open_curve_pt[i], check_only);
29760 // If at least one needs modification then break the for loop
29761 if (inner_open_boundary_update_necessary) break;
29762 }
29763 }
29764 }
29765 }
29766
29767 // Flag to indicate whether we need to adapt or not (for parallel
29768 // mesh adaptation only)
29769 int adapt_all = 0;
29770 // ------------------------------------------
29771 // DISTRIBUTED MESH: BEGIN
29772 // ------------------------------------------
29773#ifdef OOMPH_HAS_MPI
29774 // When working in distributed meshes we need to ensure that all the
29775 // processors take part on the adaptation process. If at least one
29776 // of the processors requires adaptation then all processor take
29777 // part on the adaptation process.
29778 int adapt_this_processor = 0;
29779 if (this->is_mesh_distributed())
29780 {
29781 // Do this processor requires adaptation?
29782 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29783 (min_angle < min_permitted_angle()) ||
29784 (outer_boundary_update_necessary) ||
29785 (inner_boundary_update_necessary) ||
29786 (inner_open_boundary_update_necessary))
29787 {
29788 adapt_this_processor = 1;
29789 }
29790
29791 // Get the communicator of the mesh
29792 OomphCommunicator* comm_pt = this->communicator_pt();
29793
29794 // Verify if at least one processor needs mesh adaptation
29795 MPI_Allreduce(&adapt_this_processor,
29796 &adapt_all,
29797 1,
29798 MPI_INT,
29799 MPI_SUM,
29800 comm_pt->mpi_comm());
29801 }
29802#endif
29803 // ------------------------------------------
29804 // DISTRIBUTED MESH: END
29805 // ------------------------------------------
29806
29807 // Should we bother to adapt?
29808 if ((Nrefined > 0) || (Nunrefined > max_keep_unrefined()) ||
29809 (min_angle < min_permitted_angle()) ||
29810 (outer_boundary_update_necessary) ||
29811 (inner_boundary_update_necessary) ||
29812 (inner_open_boundary_update_necessary) || (adapt_all))
29813 {
29814 if (!((Nrefined > 0) || (Nunrefined > max_keep_unrefined())))
29815 {
29816 if ((outer_boundary_update_necessary) ||
29817 (inner_boundary_update_necessary) ||
29818 (inner_open_boundary_update_necessary))
29819 {
29821 << "Mesh regeneration triggered by inaccurate interface/surface\n"
29822 << "representation; setting Nrefined to number of elements.\n"
29823 << "outer_boundary_update_necessary : "
29824 << outer_boundary_update_necessary << "\n"
29825 << "inner_boundary_update_necessary : "
29826 << inner_boundary_update_necessary << "\n"
29827 << "inner_open_boundary_update_necessary: "
29828 << inner_open_boundary_update_necessary << "\n";
29829 Nrefined = nelement();
29830 }
29831 else
29832 {
29833 oomph_info << "Mesh regeneration triggered by min angle criterion;\n"
29834 << "setting Nrefined to number of elements.\n";
29835 Nrefined = nelement();
29836 }
29837 }
29838
29839 // ------------------------------------------
29840 // DISTRIBUTED MESH: BEGIN
29841 // ------------------------------------------
29842#ifdef OOMPH_HAS_MPI
29843 else if (this->is_mesh_distributed() && adapt_this_processor == 0 &&
29844 adapt_all > 0)
29845 {
29846 oomph_info << "Mesh regeneration triggered by (" << adapt_all
29847 << ") processor(s) "
29848 << "that require(s)\n adaptation\n";
29849 }
29850#endif
29851 // ------------------------------------------
29852 // DISTRIBUTED MESH: END
29853 // ------------------------------------------
29854
29855 // ==============================================================
29856 // BEGIN: Updating of boundaries representation (unrefinement and
29857 // refinement of polylines)
29858 // ==============================================================
29859
29860 // Add the initial and final vertices of the polylines that
29861 // present connections to a list of non-delete-able vertices. The
29862 // vertices where the connections are performed cannot be deleted
29863 add_vertices_for_non_deletion();
29864
29865 // ------------------------------------------
29866 // DISTRIBUTED MESH: BEGIN
29867 // ------------------------------------------
29868#ifdef OOMPH_HAS_MPI
29869 // Synchronise connections for shared boundaries among
29870 // processors. This is required since one of the processor may noy
29871 // know that some of its shared boundaries have connections, thus
29872 // the vertices receiving the connections cannot be deleted
29873 if (this->is_mesh_distributed())
29874 {
29875 synchronize_shared_boundary_connections();
29876 }
29877#endif // #ifdef OOMPH_HAS_MPI
29878 // ------------------------------------------
29879 // DISTRIBUTED MESH: END
29880 // ------------------------------------------
29881
29882 // Are we allowing automatic insertion of vertices on boundaries?
29883 // If YES then Triangle automatically insert points along
29884 // boundaries, if NOT, then points are inserted along the
29885 // boundaries based on the target areas of boundary elements. When
29886 // the mesh is distributed the automatic insertion of vertices by
29887 // Triangle along the boundaries is not allowed
29888 if (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29889 {
29890 // Generate a new 1D mesh representation of the inner hole boundaries
29891 unsigned nhole = this->Internal_polygon_pt.size();
29892 Vector<Vector<double>> internal_point_coord(nhole);
29893 this->surface_remesh_for_inner_hole_boundaries(internal_point_coord);
29894
29895 // Update the representation of the outer boundary
29896 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29897 {
29898 this->update_polygon_using_face_mesh(
29899 this->Outer_boundary_pt[i_outer]);
29900 }
29901
29902 // After updating outer and internal closed boundaries it is also
29903 // necessary to update internal boundaries.
29904 unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29905 for (unsigned i = 0; i < n_open_polyline; i++)
29906 {
29907 this->update_open_curve_using_face_mesh(
29908 this->Internal_open_curve_pt[i]);
29909 }
29910 }
29911 else
29912 {
29913 // Update the representation of the internal boundaries using
29914 // the element's target area
29915
29916 // Get the number of interal polygons
29917 const unsigned ninternal = this->Internal_polygon_pt.size();
29918 for (unsigned i_internal = 0; i_internal < ninternal; i_internal++)
29919 {
29920 this->update_polygon_using_elements_area(
29921 this->Internal_polygon_pt[i_internal], target_area);
29922 }
29923
29924 // Update the representation of the outer boundaries using the
29925 // element's target area
29926 for (unsigned i_outer = 0; i_outer < nouter; i_outer++)
29927 {
29928 this->update_polygon_using_elements_area(
29929 this->Outer_boundary_pt[i_outer], target_area);
29930 }
29931
29932 // Update the representation of the internal open boundaries
29933 // using the element's target areas
29934 const unsigned n_open_polyline = this->Internal_open_curve_pt.size();
29935 for (unsigned i = 0; i < n_open_polyline; i++)
29936 {
29937 this->update_open_curve_using_elements_area(
29938 this->Internal_open_curve_pt[i], target_area);
29939 }
29940
29941 // ------------------------------------------
29942 // DISTRIBUTED MESH: BEGIN
29943 // ------------------------------------------
29944
29945 // When working with a distributed mesh we require to update the
29946 // boundary representation of the shared boundaries, this is
29947 // based on the target areas of the elements adjaced to the
29948 // shared boundaries
29949#ifdef OOMPH_HAS_MPI
29950 // Update shared boundaries if the mesh is distributed
29951 if (this->is_mesh_distributed())
29952 {
29953 // Get the rank of the current processor
29954 const unsigned my_rank = this->communicator_pt()->my_rank();
29955
29956 // Get the number of shared curves
29957 const unsigned n_curves = this->nshared_boundary_curves(my_rank);
29958 // Loop over the shared curves in the current processor
29959 for (unsigned nc = 0; nc < n_curves; nc++)
29960 {
29961 // Update the shared polyline
29962 this->update_shared_curve_using_elements_area(
29963 this->Shared_boundary_polyline_pt[my_rank][nc], // shared_curve,
29964 target_area);
29965 }
29966
29967 } // if (this->is_mesh_distributed())
29968#endif
29969
29970 // ------------------------------------------
29971 // DISTRIBUTED MESH: END
29972 // ------------------------------------------
29973
29974 } // else if
29975 // (this->is_automatic_creation_of_vertices_on_boundaries_allowed())
29976
29977 // ==============================================================
29978 // END: Updating of boundaries representation (unrefinement and
29979 // refinement of polylines)
29980 // ==============================================================
29981
29982 // ==============================================================
29983 // BEGIN: Reset boundary coordinates for boundaries with no
29984 // associated GeomObject
29985 // ==============================================================
29986
29987 // If there is not a geometric object associated with the boundary
29988 // then reset the boundary coordinates so that the lengths are
29989 // consistent in the new mesh and the old mesh.
29990 const unsigned n_boundary = this->nboundary();
29991
29992 const double t_start_first_stage_segments_connectivity =
29994
29995 // ------------------------------------------
29996 // DISTRIBUTED MESH: BEGIN
29997 // ------------------------------------------
29998#ifdef OOMPH_HAS_MPI
29999 // Clear storage for assignment of initial zeta values for
30000 // boundaries
30001 if (this->is_mesh_distributed())
30002 {
30003 this->Assigned_segments_initial_zeta_values.clear();
30004 }
30005#endif // #ifdef OOMPH_HAS_MPI
30006 // ------------------------------------------
30007 // DISTRIBUTED MESH: END
30008 // ------------------------------------------
30009
30010 // Loop over the boundaries to assign boundary coordinates
30011 for (unsigned b = 0; b < n_boundary; ++b)
30012 {
30013 // ------------------------------------------
30014 // DISTRIBUTED MESH: BEGIN
30015 // ------------------------------------------
30016#ifdef OOMPH_HAS_MPI
30017 if (this->is_mesh_distributed())
30018 {
30019 // In a distributed mesh, the boundaries may have been split
30020 // across processors during the distribution process, thus we
30021 // need to compute the connectivity among the segments of the
30022 // boundary to correctly assign its boundary coordinates
30023 this->compute_boundary_segments_connectivity_and_initial_zeta_values(
30024 b);
30025 }
30026#endif
30027 // ------------------------------------------
30028 // DISTRIBUTED MESH: END
30029 // ------------------------------------------
30030
30031 // Does the boundary has an associated GeomObject
30032 if (this->boundary_geom_object_pt(b) == 0)
30033 {
30034 this->template setup_boundary_coordinates<ELEMENT>(b);
30035 }
30036
30037 // ------------------------------------------
30038 // DISTRIBUTED MESH: BEGIN
30039 // ------------------------------------------
30040#ifdef OOMPH_HAS_MPI
30041 if (this->is_mesh_distributed())
30042 {
30043 // Synchronise boundary coordinates for internal open curves,
30044 // also establish the boundary coordinates for the nodes on
30045 // the corners of elements not on the boundary
30046 this->synchronize_boundary_coordinates(b);
30047 }
30048#endif
30049 // ------------------------------------------
30050 // DISTRIBUTED MESH: END
30051 // ------------------------------------------
30052
30053 } // for (b<n_boundary)
30054
30055 const double t_total_first_stage_segments_connectivity =
30056 TimingHelpers::timer() - t_start_first_stage_segments_connectivity;
30057
30058 // ==============================================================
30059 // END: Reset boundary coordinates for boundaries with no
30060 // associated GeomObject
30061 // ==============================================================
30062
30063 // ------------------------------------------
30064 // DISTRIBUTED MESH: BEGIN
30065 // ------------------------------------------
30066#ifdef OOMPH_HAS_MPI
30067 // ==============================================================
30068 // BEGIN: Create the new representation of the domain by joining
30069 // the original boundaries and the shared boundaries.
30070 // ==============================================================
30071
30072 // Storage for the new temporary polygons "closed" by the shared
30073 // boundaries
30074 Vector<TriangleMeshPolygon*> tmp_outer_polygons_pt;
30075
30076 // Storage for the new temporary open curves, could be the
30077 // original open curves or "chunks" of the original open curves
30078 // not overlapped by shared boundaries
30079 Vector<TriangleMeshOpenCurve*> tmp_open_curves_pt;
30080
30081 if (this->is_mesh_distributed())
30082 {
30083 // Create the new polygons and open curves with help of the
30084 // original polylines and shared polylines
30085 this->create_distributed_domain_representation(tmp_outer_polygons_pt,
30086 tmp_open_curves_pt);
30087
30088 // Create the connections of the temporary domain representations
30089 this->create_temporary_boundary_connections(tmp_outer_polygons_pt,
30090 tmp_open_curves_pt);
30091 }
30092 // ==============================================================
30093 // END: Create the new representation of the domain by joining
30094 // the original boundaries and the shared boundaries.
30095 // ==============================================================
30096#endif
30097 // ------------------------------------------
30098 // DISTRIBUTED MESH: END
30099 // ------------------------------------------
30100
30101 // Re-establish polylines' connections. The boundary
30102 // representation has changed (new polylines), therefore we need
30103 // to update the connection information
30104 Vector<TriangleMeshPolyLine*> resume_initial_connection_polyline_pt;
30105 Vector<TriangleMeshPolyLine*> resume_final_connection_polyline_pt;
30106 restore_boundary_connections(resume_initial_connection_polyline_pt,
30107 resume_final_connection_polyline_pt);
30108
30109 // Update the region information by setting the coordinates from the
30110 // centroid of the first element in each region (which should allow
30111 // automatic updates when the regions deform)
30112 {
30113 unsigned n_region = this->nregion();
30114 if (n_region > 1)
30115 {
30116 for (std::map<unsigned, Vector<double>>::iterator it =
30117 this->Regions_coordinates.begin();
30118 it != this->Regions_coordinates.end();
30119 ++it)
30120 {
30121 // Storage for the approximate centroid
30122 Vector<double> centroid(2, 0.0);
30123
30124 // Get the region id
30125 unsigned region_id = it->first;
30126
30127 // Report information
30128 oomph_info << "Region " << region_id << ": " << it->second[0] << " "
30129 << it->second[1] << " ";
30130
30131 // Check that there is at least one element in the region
30132 unsigned n_region_element = this->nregion_element(region_id);
30133 if (n_region_element > 0)
30134 {
30135 // Cache pointer to the first element
30136 FiniteElement* const elem_pt =
30137 this->region_element_pt(region_id, 0);
30138
30139 // Loop over the corners of the triangle and average
30140 for (unsigned n = 0; n < 3; n++)
30141 {
30142 Node* const nod_pt = elem_pt->node_pt(n);
30143 for (unsigned i = 0; i < 2; i++)
30144 {
30145 centroid[i] += nod_pt->x(i);
30146 }
30147 }
30148 for (unsigned i = 0; i < 2; i++)
30149 {
30150 centroid[i] /= 3;
30151 }
30152 // Now we have the centroid set it
30153 it->second = centroid;
30154
30155 oomph_info << " , " << it->second[0] << " " << it->second[1]
30156 << std::endl;
30157 } // end of case when there is at least one element
30158
30159 } // loop over regions coordinates
30160
30161 } // if(n_region > 1)
30162
30163 } // Updating region info.
30164
30165 // ==============================================================
30166 // BEGIN: Create background mesh
30167 // ==============================================================
30168
30169 // Are we dealing with a solid mesh?
30170 SolidMesh* solid_mesh_pt = dynamic_cast<SolidMesh*>(this);
30171
30172 // Build temporary uniform background mesh
30173 //----------------------------------------
30174 // with area set by maximum required area
30175 //---------------------------------------
30176 RefineableTriangleMesh<ELEMENT>* tmp_new_mesh_pt = 0;
30177
30178 // The storage for the new temporary boundaries representation to
30179 // create the background mesh
30180 Vector<TriangleMeshClosedCurve*> closed_curve_pt;
30182 Vector<TriangleMeshOpenCurve*> open_curves_pt;
30183
30184#ifdef OOMPH_HAS_MPI
30185 if (!this->is_mesh_distributed())
30186#endif
30187 {
30188 // Copy the outer boundaries
30189 closed_curve_pt.resize(nouter);
30190 for (unsigned i = 0; i < nouter; i++)
30191 {
30192 closed_curve_pt[i] = this->Outer_boundary_pt[i];
30193 }
30194
30195 // Copy the internal closed boundaries (may be holes)
30196 const unsigned n_holes = this->Internal_polygon_pt.size();
30197 hole_pt.resize(n_holes);
30198 for (unsigned i = 0; i < n_holes; i++)
30199 {
30200 hole_pt[i] = this->Internal_polygon_pt[i];
30201 }
30202
30203 // Copy the internal open curves
30204 const unsigned n_open_curves = this->Internal_open_curve_pt.size();
30205 open_curves_pt.resize(n_open_curves);
30206 for (unsigned i = 0; i < n_open_curves; i++)
30207 {
30208 open_curves_pt[i] = this->Internal_open_curve_pt[i];
30209 }
30210 }
30211 // ------------------------------------------
30212 // DISTRIBUTED MESH: BEGIN
30213 // ------------------------------------------
30214#ifdef OOMPH_HAS_MPI
30215 else
30216 {
30217 // Copy the new representation of the outer/internal closed
30218 // boundaries
30219 const unsigned n_tmp_outer = tmp_outer_polygons_pt.size();
30220 closed_curve_pt.resize(n_tmp_outer);
30221 for (unsigned i = 0; i < n_tmp_outer; i++)
30222 {
30223 closed_curve_pt[i] = tmp_outer_polygons_pt[i];
30224 }
30225
30226 // Copy the new representation of the internal open curves
30227 const unsigned n_open_curves = tmp_open_curves_pt.size();
30228 open_curves_pt.resize(n_open_curves);
30229 for (unsigned i = 0; i < n_open_curves; i++)
30230 {
30231 open_curves_pt[i] = tmp_open_curves_pt[i];
30232 }
30233 }
30234#endif
30235 // ------------------------------------------
30236 // DISTRIBUTED MESH: END
30237 // ------------------------------------------
30238
30239 // ----------------------------------------------------------------
30240 // Gather all the information and use the TriangleMeshParameters
30241 // object which help us on the manage of all TriangleMesh object's
30242 // information
30243
30244 // Create the TriangleMeshParameters objects with the outer boundary
30245 // as the only one parameter
30246 TriangleMeshParameters triangle_mesh_parameters(closed_curve_pt);
30247
30248 // Pass information about the holes
30249 triangle_mesh_parameters.internal_closed_curve_pt() = hole_pt;
30250
30251 // Pass information about the internal open boundaries
30252 triangle_mesh_parameters.internal_open_curves_pt() = open_curves_pt;
30253
30254 // Set the element area
30255 triangle_mesh_parameters.element_area() = max_area;
30256
30257 // Pass information about the extra holes (not defined with closed
30258 // boundaries)
30259 triangle_mesh_parameters.extra_holes_coordinates() =
30260 this->Extra_holes_coordinates;
30261
30262 // Pass information about regions
30263 triangle_mesh_parameters.regions_coordinates() =
30264 this->Regions_coordinates;
30265
30266 // Pass information about the using of regions
30267 if (this->Use_attributes)
30268 {
30269 triangle_mesh_parameters.enable_use_attributes();
30270 }
30271
30272 // Pass information about allowing the creation of new points
30273 if (!this->is_automatic_creation_of_vertices_on_boundaries_allowed())
30274 {
30275 triangle_mesh_parameters
30277 }
30278
30279 // When the mesh is distributed we need to create a distributed
30280 // background mesh
30281#ifdef OOMPH_HAS_MPI
30282 if (this->is_mesh_distributed())
30283 {
30284 // Mark the mesh to be created as distributed by passing a
30285 // pointer to the communicator
30286 triangle_mesh_parameters.set_communicator_pt(this->communicator_pt());
30287 }
30288#endif
30289
30290 // ----------------------------------------------------------
30291 // Build the background mesh using Triangle
30292 // ----------------------------------------------------------
30293 const double t_start_building_background_mesh = TimingHelpers::timer();
30294
30295 if (solid_mesh_pt != 0)
30296 {
30297 tmp_new_mesh_pt = new RefineableSolidTriangleMesh<ELEMENT>(
30298 triangle_mesh_parameters, this->Time_stepper_pt);
30299 }
30300 else
30301 {
30302 tmp_new_mesh_pt = new RefineableTriangleMesh<ELEMENT>(
30303 triangle_mesh_parameters, this->Time_stepper_pt);
30304 }
30305
30306 if (Print_timings_level_adaptation > 2)
30307 {
30308 oomph_info << "CPU for building background mesh: "
30309 << TimingHelpers::timer() - t_start_building_background_mesh
30310 << std::endl;
30311 }
30312
30313 // Pass the info. regarding the maximum and minimum element size
30314 // from the old mesh to the background mesh
30315 const double this_max_element_size = this->max_element_size();
30316 const double this_min_element_size = this->min_element_size();
30317 tmp_new_mesh_pt->max_element_size() = this_max_element_size;
30318 tmp_new_mesh_pt->min_element_size() = this_min_element_size;
30319
30320 // ... also copy the minimum permitted angle
30321 const double this_min_permitted_angle = this->min_permitted_angle();
30322 tmp_new_mesh_pt->min_permitted_angle() = this_min_permitted_angle;
30323
30324 // ------------------------------------------
30325 // DISTRIBUTED MESH: BEGIN
30326 // ------------------------------------------
30327#ifdef OOMPH_HAS_MPI
30328 // If the mesh is distributed we need to pass and set the
30329 // information of internal boundaries overlaped by shared
30330 // boundaries
30331 if (this->is_mesh_distributed())
30332 {
30333 // Check if necessary to fill boundary elements for those
30334 // internal boundaries that overlap shared boundaries
30335 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30336 {
30337 // Copy the data structures that indicates which shared
30338 // boundaries are part of an internal boundary
30340 this->shared_boundary_overlaps_internal_boundary();
30341
30342 // Copy the data structure that indicates which are the shared
30343 // boundaries in each processor
30344 tmp_new_mesh_pt->shared_boundaries_ids() =
30345 this->shared_boundaries_ids();
30346
30347 // Fill the structures for the boundary elements and face indexes
30348 // of the boundary elements
30349 tmp_new_mesh_pt
30351
30352 } // if (this->nshared_boundary_overlaps_internal_boundary() > 0)
30353
30354 } // if (this->is_mesh_distributed())
30355#endif // #ifdef OOMPH_HAS_MPI
30356 // ------------------------------------------
30357 // DISTRIBUTED MESH: END
30358 // ------------------------------------------
30359
30360 // Snap to curvilinear boundaries (some code duplication as this
30361 // is repeated below but helper function would take so many
30362 // arguments that it's nearly as messy...
30363
30364 // Pass the boundary geometric objects to the new mesh
30365 tmp_new_mesh_pt->boundary_geom_object_pt() =
30366 this->boundary_geom_object_pt();
30367
30368 // Reset the boundary coordinates if there is
30369 // a geometric object associated with the boundary
30370 tmp_new_mesh_pt->boundary_coordinate_limits() =
30371 this->boundary_coordinate_limits();
30372
30373 const double t_start_second_stage_segments_connectivity =
30375
30376 for (unsigned b = 0; b < n_boundary; b++)
30377 {
30378 // ------------------------------------------
30379 // DISTRIBUTED MESH: BEGIN
30380 // ------------------------------------------
30381#ifdef OOMPH_HAS_MPI
30382 if (this->is_mesh_distributed())
30383 {
30384 // Identify the segments of the new mesh with the ones of the
30385 // original mesh
30386 tmp_new_mesh_pt
30388 this);
30389 }
30390#endif
30391 // ------------------------------------------
30392 // DISTRIBUTED MESH: END
30393 // ------------------------------------------
30394
30395 // Setup boundary coordinates for boundaries with GeomObject
30396 // associated
30397 if (tmp_new_mesh_pt->boundary_geom_object_pt(b) != 0)
30398 {
30399 tmp_new_mesh_pt->template setup_boundary_coordinates<ELEMENT>(b);
30400 }
30401 }
30402
30403 const double t_total_second_stage_segments_connectivity =
30404 TimingHelpers::timer() - t_start_second_stage_segments_connectivity;
30405
30406 const double t_start_snap_nodes_bg_mesh = TimingHelpers::timer();
30407 // Move the nodes on the new boundary onto the old curvilinear
30408 // boundary. If the boundary is straight this will do precisely
30409 // nothing but will be somewhat inefficient
30410 for (unsigned b = 0; b < n_boundary; b++)
30411 {
30412 this->snap_nodes_onto_boundary(tmp_new_mesh_pt, b);
30413 }
30414
30415 const double t_total_snap_nodes_bg_mesh =
30416 TimingHelpers::timer() - t_start_snap_nodes_bg_mesh;
30417
30418 if (Print_timings_level_adaptation > 2)
30419 {
30420 oomph_info << "CPU for snapping nodes onto boundaries "
30421 << "(background mesh): " << t_total_snap_nodes_bg_mesh
30422 << std::endl;
30423 }
30424
30425 // Update mesh further?
30426 if (Mesh_update_fct_pt != 0)
30427 {
30428 Mesh_update_fct_pt(tmp_new_mesh_pt);
30429 }
30430
30431 // If we have a continuation problem
30432 // any problem in which the timestepper is a "generalisedtimestepper",
30433 // which will have been set by the problem, then ensure
30434 // all data in the new mesh has the appropriate timestepper
30435 /*if(dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
30436 {
30437 tmp_new_mesh_pt->set_nodal_and_elemental_time_stepper(
30438 this->Time_stepper_pt);
30439 tmp_new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt);
30440 }*/
30441
30442
30443 // tmp_new_mesh_pt->output("mesh_nodes_snapped_0.dat");
30444 // this->output("existing_mesh.dat");
30445
30446 // ==============================================================
30447 // END: Create background mesh
30448 // ==============================================================
30449
30450 // ==============================================================
30451 // BEGIN: Transferring of target areas and creation of new mesh
30452 // ==============================================================
30453
30454 // Get the TriangulateIO object associated with that mesh
30455 TriangulateIO tmp_new_triangulateio =
30456 tmp_new_mesh_pt->triangulateio_representation();
30457 RefineableTriangleMesh<ELEMENT>* new_mesh_pt = 0;
30458
30459 // If the mesh is a solid mesh then do the mapping based on the
30460 // Eulerian coordinates
30461 bool use_eulerian_coords = false;
30462 if (solid_mesh_pt != 0)
30463 {
30464 use_eulerian_coords = true;
30465 }
30466
30467
30468#ifdef OOMPH_HAS_CGAL
30469
30470 // Make cgal-based bin
30471 CGALSamplePointContainerParameters cgal_params(this);
30472 if (use_eulerian_coords)
30473 {
30475 }
30476 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(&cgal_params);
30477
30478#else
30479
30480 // Make nonrefineable bin
30482 if (use_eulerian_coords)
30483 {
30485 }
30486 Vector<unsigned> bin_dim(2);
30487 bin_dim[0] = Nbin_x_for_area_transfer;
30488 bin_dim[1] = Nbin_y_for_area_transfer;
30489 params.dimensions_of_bin_array() = bin_dim;
30490 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(&params);
30491
30492#endif
30493
30494 // Set up a map from pointer to element to its number
30495 // in the mesh
30496 std::map<GeneralisedElement*, unsigned> element_number;
30497 unsigned nelem = this->nelement();
30498 for (unsigned e = 0; e < nelem; e++)
30499 {
30500 element_number[this->element_pt(e)] = e;
30501 }
30502
30503#ifndef OOMPH_HAS_CGAL
30504
30505 // Create a vector to store the min target area of each bin (at
30506 // this stage the number of bins should not be that large, so it
30507 // should be safe to build a vector for the total number of bins)
30508 Vector<double> bin_min_target_area;
30509
30510 // Get pointer to sample point container
30511 NonRefineableBinArray* bin_array_pt =
30512 dynamic_cast<NonRefineableBinArray*>(
30513 mesh_geom_obj_pt->sample_point_container_pt());
30514 if (bin_array_pt == 0)
30515 {
30516 throw OomphLibError(
30517 "Sample point container has to be NonRefineableBinArray",
30518 OOMPH_CURRENT_FUNCTION,
30519 OOMPH_EXCEPTION_LOCATION);
30520 }
30521
30522 {
30523 unsigned n_bin = 0;
30524 unsigned max_n_entry = 0;
30525 unsigned min_n_entry = UINT_MAX;
30526 unsigned tot_n_entry = 0;
30527 unsigned n_empty = 0;
30528 bin_array_pt->get_fill_stats(
30529 n_bin, max_n_entry, min_n_entry, tot_n_entry, n_empty);
30530
30531 oomph_info << "Before bin diffusion:"
30532 << " nbin:(" << n_bin << ")"
30533 << " nempty:(" << n_empty << ")"
30534 << " min:(" << min_n_entry << ")"
30535 << " max:(" << max_n_entry << ")"
30536 << " average entries:("
30537 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30538 }
30539
30540 // Fill bin by diffusion
30541 double t0_bin_diff = TimingHelpers::timer();
30542 oomph_info << "Going into diffusion bit...\n";
30543 bin_array_pt->fill_bin_by_diffusion();
30544 oomph_info << "Back from diffusion bit...\n";
30545 oomph_info << "Time for bin diffusion: "
30546 << TimingHelpers::timer() - t0_bin_diff << std::endl;
30547
30548 // Do some stats
30549 {
30550 unsigned n_bin = 0;
30551 unsigned max_n_entry = 0;
30552 unsigned min_n_entry = UINT_MAX;
30553 unsigned tot_n_entry = 0;
30554 unsigned n_empty = 0;
30555 bin_array_pt->get_fill_stats(
30556 n_bin, max_n_entry, min_n_entry, tot_n_entry, n_empty);
30557
30558 oomph_info << "After bin diffusion:"
30559 << " nbin:(" << n_bin << ")"
30560 << " nempty:(" << n_empty << ")"
30561 << " min:(" << min_n_entry << ")"
30562 << " max:(" << max_n_entry << ")"
30563 << " average entries:("
30564 << double(tot_n_entry) / double(n_bin) << ")" << std::endl;
30565 }
30566
30567
30568 // For each bin, compute the minimum of the target areas in the bin
30569
30570 // Timing for map
30571 double t_total_map = 0.0;
30572
30573 // Counter for map
30574 unsigned counter_map = 0;
30575
30576 // Get access to the bins (we need access to the content of the
30577 // bins to compute the minimum of the target areas of the elements
30578 // in each bin)
30579 const std::map<unsigned,
30581 bins_pt = bin_array_pt->get_all_bins_content();
30582
30583 // Get the number of bins
30584 const unsigned n_bin = bins_pt->size();
30585
30586 // Create a vector to store the min target area of each bin (at
30587 // this stage the number of bins should not be that large, so it
30588 // should be safe to build a vector for the total number of bins)
30589 bin_min_target_area.resize(n_bin);
30590 for (unsigned u = 0; u < n_bin; u++)
30591 {
30592 bin_min_target_area[u] = 0.0;
30593 }
30594 // loop over the bins, get their elements and compute the minimum
30595 // target area of all of them
30596 typedef std::map<
30597 unsigned,
30599 for (IT it = bins_pt->begin(); it != bins_pt->end(); it++)
30600 {
30601 // The bin number
30602 unsigned ib = (*it).first;
30603
30604 // Get the number of elements in the bin
30605 const unsigned n_ele_bin = (*it).second.size();
30606
30607 // loop over the elements in the bin
30608 for (unsigned ee = 0; ee < n_ele_bin; ee++)
30609 {
30610 // Get ee-th element (in currrent mesh) in ib-th bin
30611 GeneralisedElement* ele_pt = (*it).second[ee].first;
30612 double t_map = TimingHelpers::timer();
30613 const unsigned ele_number = element_number[ele_pt];
30614 t_total_map += TimingHelpers::timer() - t_map;
30615
30616 // Increase the number of calls to map
30617 counter_map++;
30618
30619 // Go for smallest target area of any element in this bin to
30620 // force "one level" of refinement (the one-level-ness is
30621 // enforced below by limiting the actual reduction in area
30622 if (bin_min_target_area[ib] != 0)
30623 {
30624 bin_min_target_area[ib] =
30625 std::min(bin_min_target_area[ib], target_area[ele_number]);
30626 }
30627 else
30628 {
30629 bin_min_target_area[ib] = target_area[ele_number];
30630 }
30631
30632 } // for (ee<n_ele_bin)
30633
30634 } // for (it!=bins.end())
30635
30636 oomph_info << "CPU for map[counter=" << counter_map
30637 << "]: " << t_total_map << std::endl;
30638
30639
30640 // Optional output for debugging (keep it around!)
30641 const bool output_bins = false;
30642 if (output_bins)
30643 {
30644 unsigned length = bin_min_target_area.size();
30645 for (unsigned u = 0; u < length; u++)
30646 {
30647 oomph_info << "Bin n" << u
30648 << ",target area: " << bin_min_target_area[u] << std::endl;
30649 }
30650 }
30651
30652#endif
30653
30654
30655 // Now start iterating to refine mesh recursively
30656 //-----------------------------------------------
30657 bool done = false;
30658 unsigned iter = 0;
30659#ifdef OOMPH_HAS_MPI
30660 // The number of elements that require (un)refinement
30661 unsigned n_ele_need_refinement = 0;
30662#endif
30663
30664 // The timing for the third stage of segments connectivity
30665 double t_total_third_stage_segments_connectivity = 0.0;
30666
30667 // The timing for the transfering target areas
30668 double t_total_transfer_target_areas = 0.0;
30669
30670 // The timing for the copying of target areas
30671 double t_total_limit_target_areas = 0.0;
30672
30673 // The timing to create the new mesh
30674 double t_total_create_new_adapted_mesh = 0.0;
30675
30676 // The timing for the snapping of the nodes on the new meshes
30677 double t_total_snap_nodes = 0.0;
30678
30679 // The timing to check whether other processors need to adapt
30680 double t_total_wait_other_processors = 0.0;
30681 double t_iter = TimingHelpers::timer();
30682 while (!done)
30683 {
30684 // Accept by default but overwrite if things go wrong below
30685 done = true;
30686
30687 double t_start_transfer_target_areas = TimingHelpers::timer();
30688 double t0_loop_int_pts = TimingHelpers::timer();
30689
30690 // Loop over elements in new (tmp) mesh and visit all
30691 // its integration points. Check where it's located in the bin
30692 // structure of the current mesh and pass the target area
30693 // to the new element
30694 nelem = tmp_new_mesh_pt->nelement();
30695
30696 // Store the target areas for elements in the temporary
30697 // TriangulateIO mesh
30698 Vector<double> new_transferred_target_area(nelem, 0.0);
30699 for (unsigned e = 0; e < nelem; e++)
30700 { // start loop el
30701 ELEMENT* el_pt =
30702 dynamic_cast<ELEMENT*>(tmp_new_mesh_pt->element_pt(e));
30703 unsigned nint = el_pt->integral_pt()->nweight();
30704 for (unsigned ipt = 0; ipt < nint; ipt++)
30705 {
30706 // Get the coordinate of current point
30707 Vector<double> s(2);
30708 for (unsigned i = 0; i < 2; i++)
30709 {
30710 s[i] = el_pt->integral_pt()->knot(ipt, i);
30711 }
30712
30713 Vector<double> x(2);
30714 el_pt->interpolated_x(s, x);
30715
30716#if OOMPH_HAS_CGAL
30717
30718 // Try the five nearest sample points for Newton search
30719 // then just settle on the nearest one
30720 GeomObject* geom_obj_pt = 0;
30721 unsigned max_sample_points =
30722 Max_sample_points_for_limited_locate_zeta_during_target_area_transfer;
30723 dynamic_cast<CGALSamplePointContainer*>(
30724 mesh_geom_obj_pt->sample_point_container_pt())
30725 ->limited_locate_zeta(x, max_sample_points, geom_obj_pt, s);
30726#ifdef PARANOID
30727 if (geom_obj_pt == 0)
30728 {
30729 std::stringstream error_message;
30730 error_message << "Limited locate zeta failed for zeta = [ "
30731 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30732 throw OomphLibError(error_message.str(),
30733 OOMPH_CURRENT_FUNCTION,
30734 OOMPH_EXCEPTION_LOCATION);
30735 }
30736 else
30737 {
30738#endif
30739 FiniteElement* fe_pt = dynamic_cast<FiniteElement*>(geom_obj_pt);
30740#ifdef PARANOID
30741 if (fe_pt == 0)
30742 {
30743 std::stringstream error_message;
30744 error_message << "Cast to FE for GeomObject returned by "
30745 "limited locate zeta failed for zeta = [ "
30746 << x[0] << " " << x[1] << " ]. Makes no sense!\n";
30747 throw OomphLibError(error_message.str(),
30748 OOMPH_CURRENT_FUNCTION,
30749 OOMPH_EXCEPTION_LOCATION);
30750 }
30751 else
30752 {
30753#endif
30754 // What's the target area of the element that contains this
30755 // point
30756 double tg_area = target_area[element_number[fe_pt]];
30757
30758 // Go for smallest target area over all integration
30759 // points in new element
30760 // to force "one level" of refinement (the one-level-ness
30761 // is enforced below by limiting the actual reduction in
30762 // area
30763 if (new_transferred_target_area[e] != 0)
30764 {
30765 new_transferred_target_area[e] =
30766 std::min(new_transferred_target_area[e], tg_area);
30767 }
30768 else
30769 {
30770 new_transferred_target_area[e] = tg_area;
30771 }
30772#ifdef PARANOID
30773 }
30774 }
30775#endif
30776
30777#else
30778
30779 // Find the bin that contains that point and its contents
30780 int bin_number = 0;
30781 bin_array_pt->get_bin(x, bin_number);
30782
30783 // Did we find it?
30784 if (bin_number < 0)
30785 {
30786 // Not even within bin boundaries... odd
30787 std::stringstream error_message;
30788 error_message << "Very odd -- we're looking for a point[ " << x[0]
30789 << " " << x[1] << " ] that's not even \n"
30790 << "located within the bin boundaries.\n";
30791 throw OomphLibError(error_message.str(),
30792 "RefineableTriangleMesh::adapt()",
30793 OOMPH_EXCEPTION_LOCATION);
30794 } // if (bin_number<0)
30795 else
30796 {
30797 // Go for smallest target area of any element in this bin
30798 // to force "one level" of refinement (the one-level-ness
30799 // is enforced below by limiting the actual reduction in
30800 // area
30801 if (new_transferred_target_area[e] != 0)
30802 {
30803 new_transferred_target_area[e] =
30804 std::min(new_transferred_target_area[e],
30805 bin_min_target_area[bin_number]);
30806 }
30807 else
30808 {
30809 new_transferred_target_area[e] =
30810 bin_min_target_area[bin_number];
30811 }
30812 }
30813
30814#endif
30815
30816 } // for (ipt<nint)
30817
30818 } // for (e<nelem)
30819
30820
30821 // do some output (keep it alive!)
30822 const bool output_target_areas = false;
30823 if (output_target_areas)
30824 {
30825 unsigned length = new_transferred_target_area.size();
30826 for (unsigned u = 0; u < length; u++)
30827 {
30828 oomph_info << "Element" << u
30829 << ",target area: " << new_transferred_target_area[u]
30830 << std::endl;
30831 }
30832 }
30833 oomph_info << "Time for loop over integration points in new mesh: "
30834 << TimingHelpers::timer() - t0_loop_int_pts << std::endl;
30835
30836
30837 // {
30838 // tmp.open((Global_string_for_annotation::
30839 // String[0]+"binned_target_areas"+
30840 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30841
30842 // Vector<Vector<std::pair<FiniteElement*,Vector<double> > > >
30843 // bin_content=
30844 // mesh_geom_obj_pt->bin_content();
30845 // unsigned nbin=bin_content.size();
30846 // for (unsigned b=0;b<nbin;b++)
30847 // {
30848 // unsigned nentry=bin_content[b].size();
30849 // for (unsigned entry=0;entry<nentry;entry++)
30850 // {
30851 // FiniteElement* el_pt=bin_content[b][entry].first;
30852 // GeneralisedElement* gen_el_pt=bin_content[b][entry].first;
30853 // Vector<double> s=bin_content[b][entry].second;
30854 // Vector<double> x(2);
30855 // el_pt->interpolated_x(s,x);
30856 // unsigned e_current=element_number[gen_el_pt];
30857 // tmp << x[0] << " " << x[1] << " "
30858 // << target_area[e_current] << " "
30859 // << el_pt->size() << " "
30860 // << std::endl;
30861 // }
30862 // }
30863 // tmp.close();
30864 // }
30865
30866 const double t_sub_total_transfer_target_areas =
30867 TimingHelpers::timer() - t_start_transfer_target_areas;
30868
30869 if (Print_timings_level_adaptation > 2)
30870 {
30871 // Get the number of elements in the old mesh (this)
30872 const unsigned n_element = this->nelement();
30873 // Get the number of elements in the background mesh
30874 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30875
30876 oomph_info << "CPU for transfer of target areas "
30877 << "[n_ele_old_mesh=" << n_element
30878 << ", n_ele_background_mesh=" << n_element_background
30879 << "] (iter " << iter
30880 << "): " << t_sub_total_transfer_target_areas << std::endl;
30881 }
30882
30883 // Add the timing for tranfer of target areas
30884 t_total_transfer_target_areas += t_sub_total_transfer_target_areas;
30885
30886 // // Output mesh
30887 // tmp_new_mesh_pt->output(("intermediate_mesh"+
30888 // StringConversion::to_string(iter)+".dat").c_str());
30889
30890 // tmp.open((Global_string_for_annotation::
30891 // String[0]+"target_areas_intermediate_mesh_iter"+
30892 // StringConversion::to_string(iter)+"_"+
30893 // StringConversion::to_string(Global_unsigned::Number)+".dat").c_str());
30894
30895 const double t_start_limit_target_areas = TimingHelpers::timer();
30896
30897 // Now copy into target area for temporary mesh but limit to
30898 // the equivalent of one sub-division per iteration
30899#ifdef OOMPH_HAS_MPI
30900 unsigned n_ele_need_refinement_iter = 0;
30901#endif
30902
30903
30904 // Don't delete! Keep these around for debugging
30905 // ofstream tmp_mesh_file;
30906 // tmp_mesh_file.open("tmp_mesh_file.dat");
30907 // tmp_new_mesh_pt->output(tmp_mesh_file);
30908 // tmp_mesh_file.close();
30909 // ofstream target_areas_file;
30910 // target_areas_file.open("target_areas_file.dat");
30911
30912 const unsigned nel_new = tmp_new_mesh_pt->nelement();
30913 Vector<double> new_target_area(nel_new);
30914 for (unsigned e = 0; e < nel_new; e++)
30915 {
30916 // The finite element
30917 FiniteElement* f_ele_pt = tmp_new_mesh_pt->finite_element_pt(e);
30918
30919 // Transferred target area
30920 const double new_area = new_transferred_target_area[e];
30921 if (new_area <= 0.0)
30922 {
30923 std::ostringstream error_stream;
30924 error_stream
30925 << "This shouldn't happen! Element whose centroid is at "
30926 << (f_ele_pt->node_pt(0)->x(0) + f_ele_pt->node_pt(1)->x(0) +
30927 f_ele_pt->node_pt(2)->x(0)) /
30928 3.0
30929 << " "
30930 << (f_ele_pt->node_pt(0)->x(1) + f_ele_pt->node_pt(1)->x(1) +
30931 f_ele_pt->node_pt(2)->x(1)) /
30932 3.0
30933 << " "
30934 << " has no target area assigned\n";
30935 throw OomphLibError(error_stream.str(),
30936 OOMPH_CURRENT_FUNCTION,
30937 OOMPH_EXCEPTION_LOCATION);
30938 }
30939 else
30940 {
30941 // Limit target area to the equivalent of uniform refinement
30942 // during this stage of the iteration
30943 new_target_area[e] = new_area;
30944 if (new_target_area[e] < f_ele_pt->size() / 3.0)
30945 {
30946 new_target_area[e] = f_ele_pt->size() / 3.0;
30947
30948 // We'll need to give it another go later
30949 done = false;
30950 }
30951
30952 // Don't delete! Keep around for debugging
30953 // target_areas_file
30954 // << (f_ele_pt->node_pt(0)->x(0)+
30955 // f_ele_pt->node_pt(1)->x(0)+
30956 // f_ele_pt->node_pt(2)->x(0))/3.0 << " "
30957 // << (f_ele_pt->node_pt(0)->x(1)+
30958 // f_ele_pt->node_pt(1)->x(1)+
30959 // f_ele_pt->node_pt(2)->x(1))/3.0 << " "
30960 // << new_area << " "
30961 // << new_target_area[e] << std::endl;
30962
30963
30964#ifdef OOMPH_HAS_MPI
30965 // Keep track of the elements that require (un)refinement
30966 n_ele_need_refinement_iter++;
30967#endif
30968
30969 } // else if (new_area <= 0.0)
30970
30971 } // for (e < nel_new)
30972
30973
30974 // Don't delete! Keep around for debugging
30975 // target_areas_file.close();
30976
30977 const double t_sub_total_limit_target_areas =
30978 TimingHelpers::timer() - t_start_limit_target_areas;
30979
30980 // Add the timing for copying target areas
30981 t_total_limit_target_areas += t_sub_total_limit_target_areas;
30982
30983 if (Print_timings_level_adaptation > 2)
30984 {
30985 // Get the number of elements in the old mesh (this)
30986 const unsigned n_element = this->nelement();
30987 // Get the number of elements in the background mesh
30988 const unsigned n_element_background = tmp_new_mesh_pt->nelement();
30989
30990 oomph_info << "CPU for limiting target areas "
30991 << "[n_ele_old_mesh=" << n_element
30992 << ", n_ele_background_mesh=" << n_element_background
30993 << "] (iter " << iter
30994 << "): " << t_sub_total_limit_target_areas << std::endl;
30995 }
30996
30997 if (done)
30998 {
31000 << "All area adjustments accommodated by max. permitted area"
31001 << " reduction \n";
31002 }
31003 else
31004 {
31005 oomph_info << "NOT all area adjustments accommodated by max. "
31006 << "permitted area reduction \n";
31007 }
31008
31009 // tmp.close();
31010 // pause("doced binned_target_areas.dat and intermediate mesh targets");
31011
31012 // Now create the new mesh from TriangulateIO structure
31013 //-----------------------------------------------------
31014 // associated with uniform background mesh and the
31015 //------------------------------------------------
31016 // associated target element sizes.
31017 //---------------------------------
31018
31019 const double t_start_create_new_adapted_mesh = TimingHelpers::timer();
31020
31021 // Solid mesh?
31022 if (solid_mesh_pt != 0)
31023 {
31024 new_mesh_pt = new RefineableSolidTriangleMesh<ELEMENT>(
31025 new_target_area,
31026 tmp_new_triangulateio,
31027 this->Time_stepper_pt,
31028 this->Use_attributes,
31029 this->Allow_automatic_creation_of_vertices_on_boundaries,
31030 this->communicator_pt());
31031 }
31032 // No solid mesh
31033 else
31034 {
31035 new_mesh_pt = new RefineableTriangleMesh<ELEMENT>(
31036 new_target_area,
31037 tmp_new_triangulateio,
31038 this->Time_stepper_pt,
31039 this->Use_attributes,
31040 this->Allow_automatic_creation_of_vertices_on_boundaries,
31041 this->communicator_pt());
31042 }
31043
31044 // Sub-total to create new adapted mesh
31045 const double t_sub_total_create_new_adapted_mesh =
31046 TimingHelpers::timer() - t_start_create_new_adapted_mesh;
31047
31048 // Add the time to the total snap nodes time
31049 t_total_create_new_adapted_mesh += t_sub_total_create_new_adapted_mesh;
31050
31051 if (Print_timings_level_adaptation > 2)
31052 {
31053 // Get the number of elements of the new adapted mesh
31054 const unsigned n_element_new_adapted_mesh = new_mesh_pt->nelement();
31055
31056 oomph_info << "CPU for creation of new adapted mesh "
31057 << t_sub_total_create_new_adapted_mesh
31058 << "[nele=" << n_element_new_adapted_mesh << "] (iter "
31059 << iter << "): " << t_sub_total_create_new_adapted_mesh
31060 << std::endl;
31061 }
31062
31063#ifdef OOMPH_HAS_MPI
31064 // ------------------------------------------
31065 // DISTRIBUTED MESH: BEGIN
31066 // ------------------------------------------
31067
31068 // This section is only required if we are dealing with
31069 // distributed meshes, otherwise there are not shared boundaries
31070 // overlapping internal boundaries
31071
31072 // Check if necessary to fill boundary elements for those internal
31073 // boundaries that overlap shared boundaries
31074 if (this->nshared_boundary_overlaps_internal_boundary() > 0)
31075 {
31076 // Copy the data structures that indicate which shared
31077 // boundaries are part of an internal boundary
31079 this->shared_boundary_overlaps_internal_boundary();
31080
31081 // Copy the data structure that indicates which are the shared
31082 // boundaries in each processor
31083 new_mesh_pt->shared_boundaries_ids() = this->shared_boundaries_ids();
31084
31085 // Fill the structures for the boundary elements and face indexes
31086 // of the boundary elements
31087 new_mesh_pt
31089 }
31090 // ------------------------------------------
31091 // DISTRIBUTED MESH: END
31092 // ------------------------------------------
31093#endif // #ifdef OOMPH_HAS_MPI
31094
31095 // Snap to curvilinear boundaries (some code duplication as this
31096 // is repeated below but helper function would take so many
31097 // arguments that it's nearly as messy...
31098
31099 // Pass the boundary geometric objects to the new mesh
31100 new_mesh_pt->boundary_geom_object_pt() =
31101 this->boundary_geom_object_pt();
31102
31103 // Reset the boundary coordinates if there is
31104 // a geometric object associated with the boundary
31105 new_mesh_pt->boundary_coordinate_limits() =
31106 this->boundary_coordinate_limits();
31107
31108 const double t_start_third_stage_segments_connectivity =
31110
31111 for (unsigned b = 0; b < n_boundary; b++)
31112 {
31113 // ------------------------------------------
31114 // DISTRIBUTED MESH: BEGIN
31115 // ------------------------------------------
31116
31117 // Before setting up boundary coordinates for the new mesh we
31118 // require to identify the segments with the old mesh to
31119 // assign initial zeta values
31120#ifdef OOMPH_HAS_MPI
31121 if (this->is_mesh_distributed())
31122 {
31123 // Identify the segments of the new mesh with the ones of
31124 // the original mesh
31125 new_mesh_pt
31127 this);
31128 }
31129#endif
31130 // ------------------------------------------
31131 // DISTRIBUTED MESH: END
31132 // ------------------------------------------
31133
31134 // Setup boundary coordinates for boundaries with GeomObject
31135 // associated
31136 if (new_mesh_pt->boundary_geom_object_pt(b) != 0)
31137 {
31138 new_mesh_pt->template setup_boundary_coordinates<ELEMENT>(b);
31139 }
31140 }
31141
31142 t_total_third_stage_segments_connectivity +=
31143 TimingHelpers::timer() - t_start_third_stage_segments_connectivity;
31144
31145 const double t_start_snap_nodes_new_mesh = TimingHelpers::timer();
31146 // Move the nodes on the new boundary onto the old curvilinear
31147 // boundary. If the boundary is straight this will do precisely
31148 // nothing but will be somewhat inefficient
31149 for (unsigned b = 0; b < n_boundary; b++)
31150 {
31151 this->snap_nodes_onto_boundary(new_mesh_pt, b);
31152 }
31153
31154 const double t_sub_total_snap_nodes_new_mesh =
31155 TimingHelpers::timer() - t_start_snap_nodes_new_mesh;
31156
31157 // Add the time to the total snap nodes time
31158 t_total_snap_nodes += t_sub_total_snap_nodes_new_mesh;
31159
31160 if (Print_timings_level_adaptation > 2)
31161 {
31162 oomph_info << "CPU for snapping nodes onto boundaries (new mesh) "
31163 << "(iter " << iter
31164 << "): " << t_sub_total_snap_nodes_new_mesh << std::endl;
31165 }
31166
31167 // Update mesh further?
31168 if (Mesh_update_fct_pt != 0)
31169 {
31170 Mesh_update_fct_pt(new_mesh_pt);
31171 }
31172
31173 // If we have a continuation problem
31174 // any problem in which the timestepper is a "generalisedtimestepper",
31175 // which will have been set by the problem, then ensure
31176 // all data in the new mesh has the appropriate timestepper
31177 if (dynamic_cast<GeneralisedTimeStepper*>(this->Time_stepper_pt))
31178 {
31180 this->Time_stepper_pt, false);
31181 new_mesh_pt->set_mesh_level_time_stepper(this->Time_stepper_pt,
31182 false);
31183 }
31184
31185 // Not done: get ready for another iteration
31186 iter++;
31187 delete tmp_new_mesh_pt;
31188
31189#ifdef OOMPH_HAS_MPI
31190 // Check whether the number of elements that need (un)refinement
31191 // from the previous iteration is the same, if that is the case
31192 // then we mark this processor as done
31193 if (n_ele_need_refinement_iter == n_ele_need_refinement)
31194 {
31195 done = true;
31196 }
31197 // Update the number of elements that require further
31198 // (un)refinement
31199 n_ele_need_refinement = n_ele_need_refinement_iter;
31200#endif // #ifdef OOMPH_HAS_MPI
31201
31202 // ------------------------------------------
31203 // DISTRIBUTED MESH: BEGIN
31204 // ------------------------------------------
31205
31206 // We can only finish the iteration adaptation process if ALL
31207 // the involved processor are marked as done, otherwise, ALL
31208 // processor need to go for another iteration
31209#ifdef OOMPH_HAS_MPI
31210 if (this->is_mesh_distributed())
31211 {
31212 // Time to check whether other processors have finish to adapt
31213 const double t_start_wait_other_processors = TimingHelpers::timer();
31214
31215 // In case that the mesh is distributed it is necessary to
31216 // verify that no processor requires further refinement. If at
31217 // least one processor needs more refinement then all
31218 // processors need to go for another iteration to participate
31219 // in the communications
31220 unsigned this_processor_requires_another_iteration = 1;
31221
31222 // Is this processor done?
31223 if (done)
31224 {
31225 this_processor_requires_another_iteration = 0;
31226 }
31227 int nproc_not_done = this_processor_requires_another_iteration;
31228 // Get the communicator of the mesh
31229 OomphCommunicator* comm_pt = this->communicator_pt();
31230 // Communicate with all procesoors to check whether we need to
31231 // re-iterate
31232 MPI_Allreduce(&this_processor_requires_another_iteration,
31233 &nproc_not_done,
31234 1,
31235 MPI_UNSIGNED,
31236 MPI_SUM,
31237 comm_pt->mpi_comm());
31238 // Are all processors done?
31239 if (nproc_not_done > 0)
31240 {
31242 << "At least one processors requires further refinement. "
31243 << "Go for another iteration." << std::endl;
31244 done = false;
31245 }
31246
31247 // Total to check whether other processors have finish to
31248 // adapt
31249 const double t_sub_total_wait_other_processors =
31250 TimingHelpers::timer() - t_start_wait_other_processors;
31251
31252 // Add to the total timings to check whether other processors
31253 // need to adapt
31254 t_total_wait_other_processors += t_sub_total_wait_other_processors;
31255
31256 if (Print_timings_level_adaptation > 2)
31257 {
31258 oomph_info << "CPU for waiting other processors "
31259 << "(iter " << iter
31260 << "): " << t_sub_total_wait_other_processors
31261 << std::endl;
31262 }
31263
31264 } // if (this->is_mesh_distributed())
31265#endif
31266 // ------------------------------------------
31267 // DISTRIBUTED MESH: END
31268 // ------------------------------------------
31269
31270 if (!done)
31271 {
31272 oomph_info << "Going for another iteration. Current iteration ("
31273 << iter << ")" << std::endl;
31274
31275 // Use the new mesh as the tmp mesh
31276 tmp_new_mesh_pt = new_mesh_pt;
31277 tmp_new_triangulateio = new_mesh_pt->triangulateio_representation();
31278 }
31279
31280 } // end of iteration (while (!done))
31281
31282 // Delete the temporary geometric object representation of the
31283 // current mesh
31284 delete mesh_geom_obj_pt;
31285
31286 oomph_info << "CPU for iterative generation of new mesh (TOTAL): "
31287 << TimingHelpers::timer() - t_iter << std::endl;
31288
31289 if (Print_timings_level_adaptation > 1)
31290 {
31291 oomph_info << "-- CPU for creating new adapted meshes (TOTAL): "
31292 << t_total_create_new_adapted_mesh << std::endl;
31293
31294 oomph_info << "-- CPU for limiting target areas (TOTAL): "
31295 << t_total_limit_target_areas << std::endl;
31296
31297 oomph_info << "-- CPU for transferring target areas (TOTAL): "
31298 << t_total_transfer_target_areas << std::endl;
31299
31300 oomph_info << "-- CPU for waiting other processors (TOTAL): "
31301 << t_total_wait_other_processors << std::endl;
31302 }
31303
31304 // ==============================================================
31305 // END: Transferring of target areas and creation of new mesh
31306 // ==============================================================
31307
31308 // ==============================================================
31309 // BEGIN: Project solution from the old to the new mesh
31310 // ==============================================================
31311
31312 // Check that the projection step is not disabled
31313 if (!Disable_projection)
31314 {
31315 // Take the time for the projection step
31316 double tt_start_projection = TimingHelpers::timer();
31317
31318 // Print info. for tranfering target areas
31319 if (Print_timings_projection)
31320 {
31321 // Switch timings and stats on
31325 }
31326
31327 double t_proj = TimingHelpers::timer();
31328 oomph_info << "About to begin projection.\n";
31329
31330 // Project current solution onto new mesh
31331 //---------------------------------------
31332 ProjectionProblem<ELEMENT>* project_problem_pt =
31334
31335 // Projection requires to be enabled as distributed if working
31336 // with a distributed mesh
31337#ifdef OOMPH_HAS_MPI
31338 if (this->is_mesh_distributed())
31339 {
31340 // ------------------------------------------
31341 // DISTRIBUTED MESH: BEGIN
31342 // ------------------------------------------
31343
31344 // We need to back up the time stepper object since the
31345 // projection class creates a new one
31346 Time* backed_up_time_pt = this->Time_stepper_pt->time_pt();
31347
31348 // Set the projection problem as distributed
31349 project_problem_pt->enable_problem_distributed();
31350
31351 // Pass the time stepper to the projection problem (used when
31352 // setting multi_domain_interation)
31353 project_problem_pt->add_time_stepper_pt(this->Time_stepper_pt);
31354
31355 // Set the mesh used for the projection object
31356 project_problem_pt->mesh_pt() = new_mesh_pt;
31357 // project_problem_pt->disable_suppress_output_during_projection();
31358
31359 // Use iterative solver for projection? By default, an iterative
31360 // solver is used for the projection stage
31361 if (!this->use_iterative_solver_for_projection())
31362 {
31363 project_problem_pt->disable_use_iterative_solver_for_projection();
31364 }
31365
31366 // Do the projection
31367 project_problem_pt->project(this);
31368
31369 // Reset the time stepper object (only affects distributed meshes)
31370 this->Time_stepper_pt->time_pt() = backed_up_time_pt;
31371
31372 // ------------------------------------------
31373 // DISTRIBUTED MESH: END
31374 // ------------------------------------------
31375
31376 } // if (this->is_mesh_distributed())
31377 else
31378#endif // #ifdef OOMPH_HAS_MPI
31379 {
31380 // Set the mesh used for the projection object
31381 project_problem_pt->mesh_pt() = new_mesh_pt;
31382
31383 // project_problem_pt->disable_suppress_output_during_projection();
31384
31385 // Use iterative solver for projection? By default, an iterative
31386 // solver is used for the projection stage
31387 if (!this->use_iterative_solver_for_projection())
31388 {
31389 project_problem_pt->disable_use_iterative_solver_for_projection();
31390 }
31391
31392 // Do the projection
31393 project_problem_pt->project(this);
31394 }
31395
31396 // Reset printing info. for projection
31397 if (Print_timings_projection)
31398 {
31399 // Switch timings and stats off
31403 }
31404
31405 // Get the total time for projection
31406 const double tt_projection =
31407 TimingHelpers::timer() - tt_start_projection;
31408
31409 if (Print_timings_level_adaptation > 1)
31410 {
31411 // Get the number of elements in the old mesh (this)
31412 const unsigned n_element = this->nelement();
31413 // Get the number of elements in the new mesh
31414 const unsigned n_element_new = new_mesh_pt->nelement();
31415 oomph_info << "CPU for projection (in mesh adaptation) "
31416 << "[n_ele_old_mesh=" << n_element
31417 << ", n_ele_new_mesh=" << n_element_new
31418 << "]: " << tt_projection << std::endl;
31419
31420 // ------------------------------------------
31421 // DISTRIBUTED MESH: BEGIN
31422 // ------------------------------------------
31423#ifdef OOMPH_HAS_MPI
31424 if (this->is_mesh_distributed())
31425 {
31426 // The maximum number of elements in the mesh (over all
31427 // processors)
31428 unsigned n_this_element_new = n_element_new;
31429 unsigned n_max_element_new_global = 0;
31430 // Get the maximum number of elements over all processors
31431 MPI_Reduce(&n_this_element_new,
31432 &n_max_element_new_global,
31433 1,
31434 MPI_UNSIGNED,
31435 MPI_MAX,
31436 0,
31437 this->communicator_pt()->mpi_comm());
31438
31439 // The time for projection for this processor
31440 double tt_this_projection = tt_projection;
31441 double tt_global_min_projection = 0.0;
31442 double tt_global_max_projection = 0.0;
31443
31444 // Get the minimum and maximum time for projection
31445 MPI_Reduce(&tt_this_projection,
31446 &tt_global_min_projection,
31447 1,
31448 MPI_DOUBLE,
31449 MPI_MIN,
31450 0,
31451 this->communicator_pt()->mpi_comm());
31452 MPI_Reduce(&tt_this_projection,
31453 &tt_global_max_projection,
31454 1,
31455 MPI_DOUBLE,
31456 MPI_MAX,
31457 0,
31458 this->communicator_pt()->mpi_comm());
31459
31460 if (this->communicator_pt()->my_rank() == 0)
31461 {
31462 oomph_info << "CPU for projection global (MIN): "
31463 << tt_global_min_projection << std::endl;
31464 oomph_info << "CPU for projection global (MAX) "
31465 << "[n_max_ele_new_global=" << n_max_element_new_global
31466 << "]: " << tt_global_max_projection << std::endl;
31467
31468 std::cerr << "CPU for projection global (MIN): "
31469 << tt_global_min_projection << std::endl;
31470 std::cerr << "CPU for projection global (MAX): "
31471 << "[n_max_ele_new_global=" << n_max_element_new_global
31472 << "]: " << tt_global_max_projection << std::endl;
31473 }
31474 }
31475#endif // #ifdef OOMPH_HAS_MPI
31476 // ------------------------------------------
31477 // DISTRIBUTED MESH: END
31478 // ------------------------------------------
31479
31480 } // if (Print_timings_level_adaptation>1)
31481
31482 oomph_info << "CPU for projection of solution onto new mesh: "
31483 << TimingHelpers::timer() - t_proj << std::endl;
31484
31485 // Delete the projection problem
31486 delete project_problem_pt;
31487
31488 } // if (!Disable_projection)
31489 else
31490 {
31491 oomph_info << "Projection disabled! The new mesh will contain zeros"
31492 << std::endl;
31493 }
31494
31495 // ==============================================================
31496 // END: Project solution from the old to the new mesh
31497 // ==============================================================
31498
31499 double t_rest = TimingHelpers::timer();
31500
31501 // Flush the old mesh
31502 unsigned nnod = nnode();
31503 for (unsigned j = nnod; j > 0; j--)
31504 {
31505 delete Node_pt[j - 1];
31506 Node_pt[j - 1] = 0;
31507 }
31508 unsigned nel = nelement();
31509 for (unsigned e = nel; e > 0; e--)
31510 {
31511 delete Element_pt[e - 1];
31512 Element_pt[e - 1] = 0;
31513 }
31514
31515 // Now copy back to current mesh
31516 //------------------------------
31517 nnod = new_mesh_pt->nnode();
31518 Node_pt.resize(nnod);
31519 nel = new_mesh_pt->nelement();
31520 Element_pt.resize(nel);
31521 for (unsigned j = 0; j < nnod; j++)
31522 {
31523 Node_pt[j] = new_mesh_pt->node_pt(j);
31524 }
31525 for (unsigned e = 0; e < nel; e++)
31526 {
31527 Element_pt[e] = new_mesh_pt->element_pt(e);
31528 }
31529
31530 // Copy the boundary elements information from the new mesh to the
31531 // original mesh
31532 unsigned nbound = 0;
31533
31534#ifdef OOMPH_HAS_MPI
31535 // If working with a distributed mesh we need to change the number
31536 // of boundaries so that shared boundaries information is also
31537 // copied from the old to the new mesh
31538 if (this->is_mesh_distributed())
31539 {
31540 // The boundaries to be copied include those new ones in the new
31541 // mesh (shared boundaries). This info. is required to
31542 // re-establish the halo/haloed scheme
31543 nbound = new_mesh_pt->nboundary();
31544 // After halo and haloed scheme has been re-established the
31545 // number of boundaries is changed to the original number of
31546 // boundaries
31547 }
31548 else
31549#endif
31550 {
31551 // The original number of boundaries
31552 nbound = n_boundary;
31553 }
31554
31555 Boundary_element_pt.resize(nbound);
31556 Face_index_at_boundary.resize(nbound);
31557 Boundary_node_pt.resize(nbound);
31558 for (unsigned b = 0; b < nbound; b++)
31559 {
31560 unsigned nel = new_mesh_pt->nboundary_element(b);
31561 Boundary_element_pt[b].resize(nel);
31562 Face_index_at_boundary[b].resize(nel);
31563 for (unsigned e = 0; e < nel; e++)
31564 {
31565 Boundary_element_pt[b][e] = new_mesh_pt->boundary_element_pt(b, e);
31566 Face_index_at_boundary[b][e] =
31567 new_mesh_pt->face_index_at_boundary(b, e);
31568 }
31569 unsigned nnod = new_mesh_pt->nboundary_node(b);
31570 Boundary_node_pt[b].resize(nnod);
31571 for (unsigned j = 0; j < nnod; j++)
31572 {
31573 Boundary_node_pt[b][j] = new_mesh_pt->boundary_node_pt(b, j);
31574 }
31575 }
31576
31577 // Also copy over the new boundary and region information
31578 unsigned n_region = new_mesh_pt->nregion();
31579 // Only bother if we have regions
31580 if (n_region > 1)
31581 {
31582 // Deal with the region information first
31583 this->Region_attribute.resize(n_region);
31584 for (unsigned r = 0; r < n_region; r++)
31585 {
31586 this->Region_attribute[r] = new_mesh_pt->region_attribute(r);
31587 // Get the region id
31588 unsigned r_id = static_cast<unsigned>(this->Region_attribute[r]);
31589 // Find the number of elements in the region
31590 unsigned n_region_element = new_mesh_pt->nregion_element(r_id);
31591 this->Region_element_pt[r_id].resize(n_region_element);
31592 for (unsigned e = 0; e < n_region_element; e++)
31593 {
31594 this->Region_element_pt[r_id][e] =
31595 new_mesh_pt->region_element_pt(r_id, e);
31596 }
31597 }
31598
31599 // Now the boundary region information
31600 this->Boundary_region_element_pt.resize(nbound);
31601 this->Face_index_region_at_boundary.resize(nbound);
31602
31603 // Now loop over the boundaries
31604 for (unsigned b = 0; b < nbound; ++b)
31605 {
31606 for (unsigned rr = 0; rr < n_region; rr++)
31607 {
31608 // The region id
31609 unsigned r = static_cast<unsigned>(this->Region_attribute[rr]);
31610
31611 unsigned n_boundary_el_in_region =
31612 new_mesh_pt->nboundary_element_in_region(b, r);
31613
31614 if (n_boundary_el_in_region > 0)
31615 {
31616 // Allocate storage in the map
31617 this->Boundary_region_element_pt[b][r].resize(
31618 n_boundary_el_in_region);
31619 this->Face_index_region_at_boundary[b][r].resize(
31620 n_boundary_el_in_region);
31621
31622 // Copy over the information
31623 for (unsigned e = 0; e < n_boundary_el_in_region; ++e)
31624 {
31625 this->Boundary_region_element_pt[b][r][e] =
31626 new_mesh_pt->boundary_element_in_region_pt(b, r, e);
31627 this->Face_index_region_at_boundary[b][r][e] =
31628 new_mesh_pt->face_index_at_boundary_in_region(b, r, e);
31629 }
31630 }
31631 }
31632 } // End of loop over boundaries
31633
31634 } // End of case when more than one region
31635
31636 // ------------------------------------------
31637 // DISTRIBUTED MESH: BEGIN
31638 // ------------------------------------------
31639
31640 // Re-generate halo(ed) information (only for distributed meshes)
31641#ifdef OOMPH_HAS_MPI
31642 if (this->is_mesh_distributed())
31643 {
31644 // Delete halo(ed) information in the original mesh, the new
31645 // halo(ed) information is generated usign the info. of the new
31646 // mesh
31647 if (this->is_mesh_distributed())
31648 {
31649 this->Halo_node_pt.clear();
31650 this->Root_halo_element_pt.clear();
31651
31652 this->Haloed_node_pt.clear();
31653 this->Root_haloed_element_pt.clear();
31654
31655 this->External_halo_node_pt.clear();
31656 this->External_halo_element_pt.clear();
31657
31658 this->External_haloed_node_pt.clear();
31659 this->External_haloed_element_pt.clear();
31660 }
31661
31662 // Re-establish the shared boundary elements and nodes scheme
31663 // before re-establish halo(ed) information
31664 this->reset_shared_boundary_elements_and_nodes();
31665
31666 // -------------------------------------------------------------
31667 // Remove shared boundary elements and nodes from original
31668 // boundary elements and boundary nodes containers. Shared
31669 // boundary elements and nodes are stored in a special
31670 // container.
31671
31672 // Get the shared boundaries in this processor with any other
31673 // processor
31674 Vector<unsigned> my_rank_shared_boundaries_ids;
31675 this->shared_boundaries_in_this_processor(
31676 my_rank_shared_boundaries_ids);
31677
31678 // Get the number of shared boundaries
31679 const unsigned nmy_rank_shd_bnd = my_rank_shared_boundaries_ids.size();
31680 // Loop over the shared boundaries marked as original boundaries
31681 // in tmp_new_mesh
31682 for (unsigned i = 0; i < nmy_rank_shd_bnd; i++)
31683 {
31684 // Get the boundary id
31685 const unsigned shd_bnd_id = my_rank_shared_boundaries_ids[i];
31686 // Flush any previous relation of shared boundary elements
31687 // marked as original boundary elements in tmp_new_mesh
31688 this->Boundary_element_pt[shd_bnd_id].clear();
31689
31690 // Get the number of nodes associated with the original
31691 // boundary in tmp_new_mesh that is a shared boundary
31692 const unsigned tmp_nnodes = this->nshared_boundary_node(shd_bnd_id);
31693 for (unsigned n = 0; n < tmp_nnodes; n++)
31694 {
31695 Node* tmp_node_pt = this->boundary_node_pt(shd_bnd_id, n);
31696 tmp_node_pt->remove_from_boundary(shd_bnd_id);
31697 } // for (n < nnodes)
31698
31699 } // for (shd_bnd_id < nmy_rank_shd_bnd)
31700
31701 // Re-set the number of boundaries to the original one
31702 this->set_nboundary(n_boundary);
31703
31704 // Sort the nodes on the boundaries so that they have the same
31705 // order on all the boundaries
31706 this->sort_nodes_on_shared_boundaries();
31707
31708 // Re-set the halo(ed) scheme
31709 this->reset_halo_haloed_scheme();
31710
31711 // Set the correct number of segments for the boundaries with
31712 // geom objects associated
31713 for (unsigned b = 0; b < n_boundary; b++)
31714 {
31715 if (this->boundary_geom_object_pt(b) != 0)
31716 {
31717 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
31718 this->set_nboundary_segment_node(b, nsegments);
31719 }
31720 }
31721
31722 // Resume the connections in boundaries were it was suspended
31723 resume_boundary_connections(resume_initial_connection_polyline_pt,
31724 resume_final_connection_polyline_pt);
31725
31726 } // if (this->is_mesh_distributed())
31727
31728#endif // #ifdef OOMPH_HAS_MPI
31729
31730 // ------------------------------------------
31731 // DISTRIBUTED MESH: END
31732 // ------------------------------------------
31733
31734 // Snap the newly created nodes onto any geometric objects
31735 this->snap_nodes_onto_geometric_objects();
31736
31737 // Copy the IDs of the vertex nodes
31738 this->Oomph_vertex_nodes_id = new_mesh_pt->oomph_vertex_nodes_id();
31739
31740 // Copy TriangulateIO representation
31741 TriangleHelper::clear_triangulateio(this->Triangulateio);
31742 bool quiet = true;
31743 this->Triangulateio =
31745 new_mesh_pt->triangulateio_representation(), quiet);
31746
31747 // Flush the mesh
31748 new_mesh_pt->flush_element_and_node_storage();
31749
31750 // Delete the mesh
31751 delete new_mesh_pt;
31752
31753 // Resume of timings
31754 if (Print_timings_level_adaptation > 2)
31755 {
31756 // Report timings related with setting boundary coordinates of
31757 // nodes on segments
31758 oomph_info << "CPU for segments connectivity (first stage) [sec]: "
31759 << t_total_first_stage_segments_connectivity << std::endl;
31760 oomph_info << "CPU for segments connectivity (second stage) [sec]: "
31761 << t_total_second_stage_segments_connectivity << std::endl;
31762 oomph_info << "CPU for segments connectivity (third stage) [sec]: "
31763 << t_total_third_stage_segments_connectivity << std::endl;
31764 }
31765
31766 if (Print_timings_level_adaptation > 1)
31767 {
31768 const double t_total_segments_connectivity =
31769 t_total_first_stage_segments_connectivity +
31770 t_total_second_stage_segments_connectivity +
31771 t_total_third_stage_segments_connectivity;
31772
31773 oomph_info << "CPU for segments connectivity (TOTAL) [sec]: "
31774 << t_total_segments_connectivity << std::endl;
31775
31776 if (Print_timings_level_adaptation > 2)
31777 {
31778 // Report timings for snapping of nodes onto boundaries
31779 oomph_info << "CPU for snapping nodes onto boundaries "
31780 << "(new mesh): " << t_total_snap_nodes << std::endl;
31781 }
31782
31783 t_total_snap_nodes += t_total_snap_nodes_bg_mesh;
31784 oomph_info << "CPU for snapping nodes onto boundaries (TOTAL): "
31785 << t_total_snap_nodes << std::endl;
31786 }
31787
31788 double max_area = 0.0;
31789 double min_area = 0.0;
31790
31791 this->max_and_min_element_size(max_area, min_area);
31792 oomph_info << "Max/min element size in adapted mesh: " << max_area << " "
31793 << min_area << std::endl;
31794
31795 oomph_info << "CPU time for final bits [sec]: "
31796 << TimingHelpers::timer() - t_rest << std::endl;
31797 }
31798 else
31799 {
31800 oomph_info << "Not enough benefit in adaptation.\n";
31801 Nrefined = 0;
31802 Nunrefined = 0;
31803 }
31804
31805 double CPU_for_adaptation = TimingHelpers::timer() - t_start_overall;
31806 oomph_info << "CPU time for adaptation [sec]: " << CPU_for_adaptation
31807 << std::endl;
31808
31809 // ------------------------------------------
31810 // DISTRIBUTED MESH: BEGIN
31811 // ------------------------------------------
31812#ifdef OOMPH_HAS_MPI
31813 if (this->is_mesh_distributed())
31814 {
31815 // Get the communicator
31816 OomphCommunicator* comm_pt = this->communicator_pt();
31817 // Get the total number of processors to compute the average
31818 const unsigned n_proc = comm_pt->nproc();
31819 if (Print_timings_level_adaptation > 1 && n_proc > 1)
31820 {
31821 double global_min_CPU_for_adaptation = 0.0;
31822 double global_max_CPU_for_adaptation = 0.0;
31823 double global_average_CPU_for_adaptation = 0.0;
31824
31825 // Get the maximum and minimum of the adaptation times
31826 MPI_Reduce(&CPU_for_adaptation,
31827 &global_min_CPU_for_adaptation,
31828 1,
31829 MPI_DOUBLE,
31830 MPI_MIN,
31831 0,
31832 comm_pt->mpi_comm());
31833 MPI_Reduce(&CPU_for_adaptation,
31834 &global_max_CPU_for_adaptation,
31835 1,
31836 MPI_DOUBLE,
31837 MPI_MAX,
31838 0,
31839 comm_pt->mpi_comm());
31840 MPI_Reduce(&CPU_for_adaptation,
31841 &global_average_CPU_for_adaptation,
31842 1,
31843 MPI_DOUBLE,
31844 MPI_SUM,
31845 0,
31846 comm_pt->mpi_comm());
31847
31848 // Get the rank of the processor
31849 const unsigned my_rank = comm_pt->my_rank();
31850 if (my_rank == 0)
31851 {
31852 oomph_info << "CPU for adaptation (MIN): "
31853 << global_min_CPU_for_adaptation << std::endl;
31854 oomph_info << "CPU for adaptation (MAX): "
31855 << global_max_CPU_for_adaptation << std::endl;
31856 oomph_info << "CPU for adaptation (AVERAGE): "
31857 << global_average_CPU_for_adaptation / n_proc << std::endl;
31858 } // if (my_rank==0)
31859
31860 } // if (Print_timings_level_adaptation>1&&n_proc>1)
31861
31862 } // if (this->is_mesh_distributed())
31863
31864 // ------------------------------------------
31865 // DISTRIBUTED MESH: END
31866 // ------------------------------------------
31867
31868#endif // #ifdef OOMPH_HAS_MPI
31869 }
31870
31871 //=========================================================================
31872 /// \ short Mark the vertices that are not allowed for deletion by
31873 /// the unrefienment/refinement polyline methods. In charge of
31874 /// filling the Boundary_connections_pt structure
31875 //=========================================================================
31876 template<class ELEMENT>
31878 {
31879 // Clear any previous information
31880 // Boundary_chunk_connections_pt.clear();
31881 Boundary_connections_pt.clear();
31882
31883 // Loop over the boundaries in the domain (outer, internal -- closed
31884 // and open ---, and shared) and get the boundaries ids with
31885 // connections (have or receive)
31886
31887 // Store the boundaries ids that have or receive connection
31888 std::set<unsigned> boundary_id_with_connections;
31889
31890 // ------------------------------------------------------------------
31891 // Outer boundaries
31892 // ------------------------------------------------------------------
31893
31894 // Get the number of outer boundaries (closed boundaries)
31895 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
31896
31897 // Loop over the outer boundaries
31898 for (unsigned i = 0; i < n_outer_boundaries; i++)
31899 {
31900 // Get a temporary polygon representation
31901 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
31902 // Get the number of polylines associated to the current outer
31903 // boundary
31904 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31905 // Loop over the polylines
31906 for (unsigned p = 0; p < n_polyline; p++)
31907 {
31908 // Get a temporary representation of the polyline
31909 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
31910
31911 // Is the initial vertex connected?
31912 if (tmp_polyline_pt->is_initial_vertex_connected())
31913 {
31914 // Get the boundary id of the current polyline
31915 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31916
31917 // Include the boundary id to the set of boundaries with
31918 // connections
31919 boundary_id_with_connections.insert(bnd_id);
31920
31921 // Boundary id to which the curve is connecte
31922 const unsigned dst_bnd_id =
31923 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31924
31925 // Include the destination boundary id to the set of
31926 // boundaries with connections
31927 boundary_id_with_connections.insert(dst_bnd_id);
31928
31929 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31930
31931 // Is the final vertex connected?
31932 if (tmp_polyline_pt->is_final_vertex_connected())
31933 {
31934 // Get the boundary id of the current polyline
31935 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31936
31937 // Include the boundary id to the set of boundaries with
31938 // connections
31939 boundary_id_with_connections.insert(bnd_id);
31940
31941 // Boundary id to which the curve is connected
31942 const unsigned dst_bnd_id =
31943 tmp_polyline_pt->final_vertex_connected_bnd_id();
31944
31945 // Include the destination boundary id to the set of
31946 // boundaries with connections
31947 boundary_id_with_connections.insert(dst_bnd_id);
31948
31949 } // if (tmp_polyline_pt->is_final_vertex_connected())
31950
31951 } // for (p < n_polyline)
31952
31953 } // for (i < n_outer_boundaries)
31954
31955 // ------------------------------------------------------------------
31956 // Internal boundaries
31957 // ------------------------------------------------------------------
31958
31959 // Get the number of internal boundaries (closed boundaries)
31960 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
31961
31962 // Loop over the internal boundaries
31963 for (unsigned i = 0; i < n_internal_boundaries; i++)
31964 {
31965 // Get a temporary polygon representation
31966 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
31967 // Get the number of polylines associated to the current internal
31968 // boundary
31969 const unsigned n_polyline = tmp_polygon_pt->npolyline();
31970 // Loop over the polylines
31971 for (unsigned p = 0; p < n_polyline; p++)
31972 {
31973 // Get a temporary representation of the polyline
31974 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
31975
31976 // Is the initial vertex connected?
31977 if (tmp_polyline_pt->is_initial_vertex_connected())
31978 {
31979 // Get the boundary id of the current polyline
31980 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
31981
31982 // Include the boundary id to the set of boundaries with
31983 // connections
31984 boundary_id_with_connections.insert(bnd_id);
31985
31986 // Boundary id to which the curve is connecte
31987 const unsigned dst_bnd_id =
31988 tmp_polyline_pt->initial_vertex_connected_bnd_id();
31989
31990 // Include the destination boundary id to the set of
31991 // boundaries with connections
31992 boundary_id_with_connections.insert(dst_bnd_id);
31993
31994 } // if (tmp_polyline_pt->is_initial_vertex_connected())
31995
31996 // Is the final vertex connected?
31997 if (tmp_polyline_pt->is_final_vertex_connected())
31998 {
31999 // Get the boundary id of the current polyline
32000 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32001
32002 // Include the boundary id to the set of boundaries with
32003 // connections
32004 boundary_id_with_connections.insert(bnd_id);
32005
32006 // Boundary id to which the curve is connected
32007 const unsigned dst_bnd_id =
32008 tmp_polyline_pt->final_vertex_connected_bnd_id();
32009
32010 // Include the destination boundary id to the set of
32011 // boundaries with connections
32012 boundary_id_with_connections.insert(dst_bnd_id);
32013
32014 } // if (tmp_polyline_pt->is_final_vertex_connected())
32015
32016 } // for (p < n_polyline)
32017
32018 } // for (i < n_internal_boundaries)
32019
32020 // ------------------------------------------------------------------
32021 // Open boundaries (nonclosed internal boundaries)
32022 // ------------------------------------------------------------------
32023
32024 // Get the number of internal boundaries (open boundaries)
32025 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
32026
32027 // Loop over the internal open boundaries
32028 for (unsigned i = 0; i < n_open_boundaries; i++)
32029 {
32030 // Get a temporary representation for the open curve
32031 TriangleMeshOpenCurve* tmp_open_curve_pt =
32032 this->Internal_open_curve_pt[i];
32033
32034 // Get the number of curve sections associated to the current
32035 // internal open boundary
32036 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32037
32038 // Loop over the curve section
32039 for (unsigned p = 0; p < n_curve_section; p++)
32040 {
32041 // Get a temporary representation of the curve section
32042 // (polyline)
32043 TriangleMeshPolyLine* tmp_polyline_pt =
32044 tmp_open_curve_pt->polyline_pt(p);
32045
32046 // Is the initial vertex connected?
32047 if (tmp_polyline_pt->is_initial_vertex_connected())
32048 {
32049 // Get the boundary id of the current polyline
32050 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32051
32052 // Include the boundary id to the set of boundaries with
32053 // connections
32054 boundary_id_with_connections.insert(bnd_id);
32055
32056 // Boundary id to which the curve is connecte
32057 const unsigned dst_bnd_id =
32058 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32059
32060 // Include the destination boundary id to the set of
32061 // boundaries with connections
32062 boundary_id_with_connections.insert(dst_bnd_id);
32063
32064 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32065
32066 // Is the final vertex connected?
32067 if (tmp_polyline_pt->is_final_vertex_connected())
32068 {
32069 // Get the boundary id of the current polyline
32070 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32071
32072 // Include the boundary id to the set of boundaries with
32073 // connections
32074 boundary_id_with_connections.insert(bnd_id);
32075
32076 // Boundary id to which the curve is connected
32077 const unsigned dst_bnd_id =
32078 tmp_polyline_pt->final_vertex_connected_bnd_id();
32079
32080 // Include the destination boundary id to the set of
32081 // boundaries with connections
32082 boundary_id_with_connections.insert(dst_bnd_id);
32083
32084 } // if (tmp_polyline_pt->is_final_vertex_connected())
32085
32086 } // for (p < n_curve_section)
32087
32088 } // for (i < n_open_boundaries)
32089
32090#ifdef OOMPH_HAS_MPI
32091 // ------------------------------------------------------------------
32092 // Shared boundaries (only for distributed meshes)
32093 // ------------------------------------------------------------------
32094
32095 // Check if we need to include any information associated with
32096 // shared boundaries
32097 if (this->is_mesh_distributed())
32098 {
32099 // Get the rank of the current processor
32100 const unsigned my_rank = this->communicator_pt()->my_rank();
32101
32102 // Get the number of shared curves in the current processor
32103 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32104
32105 // Loop over the shared curves
32106 for (unsigned i = 0; i < n_shared_curves; i++)
32107 {
32108 // Get the number of polylines associated to the current shared
32109 // curve
32110 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32111
32112 // Loop over the polylines associated to the current shared
32113 // curve
32114 for (unsigned p = 0; p < n_polyline; p++)
32115 {
32116 // Get a temporary representation of the shared polyline
32117 TriangleMeshPolyLine* tmp_polyline_pt =
32118 this->shared_boundary_polyline_pt(my_rank, i, p);
32119
32120 // Is the initial vertex connected?
32121 if (tmp_polyline_pt->is_initial_vertex_connected())
32122 {
32123 // Get the boundary id of the current polyline
32124 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32125
32126 // Include the boundary id to the set of boundaries with
32127 // connections
32128 boundary_id_with_connections.insert(bnd_id);
32129
32130 // Boundary id to which the curve is connecte
32131 const unsigned dst_bnd_id =
32132 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32133
32134 // Include the destination boundary id to the set of
32135 // boundaries with connections
32136 boundary_id_with_connections.insert(dst_bnd_id);
32137
32138 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32139
32140 // Is the final vertex connected?
32141 if (tmp_polyline_pt->is_final_vertex_connected())
32142 {
32143 // Get the boundary id of the current polyline
32144 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32145
32146 // Include the boundary id to the set of boundaries with
32147 // connections
32148 boundary_id_with_connections.insert(bnd_id);
32149
32150 // Boundary id to which the curve is connected
32151 const unsigned dst_bnd_id =
32152 tmp_polyline_pt->final_vertex_connected_bnd_id();
32153
32154 // Include the destination boundary id to the set of
32155 // boundaries with connections
32156 boundary_id_with_connections.insert(dst_bnd_id);
32157
32158 } // if (tmp_polyline_pt->is_final_vertex_connected())
32159
32160 } // for (p < n_polyline)
32161
32162 } // for (i < n_shared_curves)
32163
32164 } // if (this->is_mesh_distributed())
32165
32166#endif // #ifdef OOMPH_HAS_MPI
32167
32168 // ---------------------------------------------------------------
32169 // Get the nodes sorted by segments of the boundaries with
32170 // connections
32171
32172 // Store the sorted nodes by segments of the boundaries with
32173 // connections
32174 std::map<unsigned, Vector<Vector<Node*>>> bnd_sorted_segment_node_pt;
32175
32176 // Loop over the boundaries with connections
32177 for (std::set<unsigned>::iterator it = boundary_id_with_connections.begin();
32178 it != boundary_id_with_connections.end();
32179 it++)
32180 {
32181 // Get the boundary id
32182 const unsigned bnd_id = (*it);
32183#ifdef OOMPH_HAS_MPI
32184 // Working with a distributed mesh
32185 if (this->is_mesh_distributed())
32186 {
32187 // Get the initial shared boundary id
32188 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
32189 // Is an original or shared boundary
32190 if (bnd_id >= init_shd_bnd_id)
32191 {
32192 // Is a shared boundary
32193
32194 // Temporary storage for the nodes on the shared boundary
32195 Vector<Vector<Node*>> tmp_shared_nodes_pt;
32196
32197 // Get the nodes associated to the shared boundary
32198 get_shared_boundary_segment_nodes_helper(bnd_id, tmp_shared_nodes_pt);
32199
32200 // Store the nodes associated to the shared boundary
32201 bnd_sorted_segment_node_pt[bnd_id] = tmp_shared_nodes_pt;
32202
32203 } // if (bnd_id >= init_shd_bnd_id)
32204 else
32205 {
32206 // Is an original boundary
32207
32208 // Temporary storage for the nodes on the original boundary
32209 Vector<Vector<Node*>> tmp_boundary_nodes_pt;
32210
32211 // Get the nodes associated to the shared boundary
32212 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32213
32214 // Store the nodes associated to the shared boundary
32215 bnd_sorted_segment_node_pt[bnd_id] = tmp_boundary_nodes_pt;
32216
32217 } // if (bnd_id >= init_shd_bnd_id)
32218
32219 } // if (this->is_mesh_distributed())
32220 else
32221#endif // #ifdef OOMPH_HAS_MPI
32222 {
32223 // Is an original boundary
32224
32225 // Temporary storage for the nodes on the original boundary
32226 Vector<Vector<Node*>> tmp_boundary_nodes_pt;
32227
32228 // Get the nodes associated to the shared boundary
32229 get_boundary_segment_nodes_helper(bnd_id, tmp_boundary_nodes_pt);
32230
32231 // Store the nodes associated to the shared boundary
32232 bnd_sorted_segment_node_pt[bnd_id] = tmp_boundary_nodes_pt;
32233
32234 } // if (this->is_mesh_distributed())
32235
32236 } // Loop over boundaries with connections
32237
32238 // -----------------------------------------------------------------
32239 // Loop again over the boundaries (original and shared) and search
32240 // for the repeated nodes in those boundaries with connections
32241
32242 // ------------------------------------------------------------------
32243 // Outer boundaries
32244 // ------------------------------------------------------------------
32245 // Loop over the outer boundaries
32246 for (unsigned i = 0; i < n_outer_boundaries; i++)
32247 {
32248 // Get a temporary polygon representation
32249 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
32250 // Get the number of polylines associated to the current outer
32251 // boundary
32252 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32253 // Loop over the polylines
32254 for (unsigned p = 0; p < n_polyline; p++)
32255 {
32256 // Get a temporary representation of the polyline
32257 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
32258
32259 // Is the initial vertex connected?
32260 if (tmp_polyline_pt->is_initial_vertex_connected())
32261 {
32262 // Get the boundary id of the current polyline
32263 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32264
32265 // Boundary id to which the curve is connected
32266 const unsigned dst_bnd_id =
32267 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32268
32269 // Boundary chunk to which the curve is connected
32270 const unsigned dst_chunk =
32271 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32272
32273 // Get the nodes representation of the current boundary
32274 Vector<Vector<Node*>> src_bnd_node_pt =
32275 bnd_sorted_segment_node_pt[bnd_id];
32276
32277 // Get the nodes representation of the boundary to connect
32278 Vector<Vector<Node*>> dst_bnd_node_pt =
32279 bnd_sorted_segment_node_pt[dst_bnd_id];
32280
32281 // Add the repeated node to the list of non delete-able
32282 // vertices
32283 add_non_delete_vertices_from_boundary_helper(
32284 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32285
32286 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32287
32288 // Is the final vertex connected?
32289 if (tmp_polyline_pt->is_final_vertex_connected())
32290 {
32291 // Get the boundary id of the current polyline
32292 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32293
32294 // Boundary id to which the curve is connected
32295 const unsigned dst_bnd_id =
32296 tmp_polyline_pt->final_vertex_connected_bnd_id();
32297
32298 // Boundary chunk to which the curve is connected
32299 const unsigned dst_chunk =
32300 tmp_polyline_pt->final_vertex_connected_n_chunk();
32301
32302 // Get the nodes representation of the current boundary
32303 Vector<Vector<Node*>> src_bnd_node_pt =
32304 bnd_sorted_segment_node_pt[bnd_id];
32305
32306 // Get the nodes representation of the boundary to connect
32307 Vector<Vector<Node*>> dst_bnd_node_pt =
32308 bnd_sorted_segment_node_pt[dst_bnd_id];
32309
32310 // Add the repeated node to the list of non delete-able
32311 // vertices
32312 add_non_delete_vertices_from_boundary_helper(
32313 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32314
32315 } // if (tmp_polyline_pt->is_final_vertex_connected())
32316
32317 } // for (p < n_polyline)
32318
32319 } // for (i < n_outer_boundaries)
32320
32321 // ------------------------------------------------------------------
32322 // Internal boundaries
32323 // ------------------------------------------------------------------
32324 // Loop over the internal boundaries
32325 for (unsigned i = 0; i < n_internal_boundaries; i++)
32326 {
32327 // Get a temporary polygon representation
32328 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
32329 // Get the number of polylines associated to the current internal
32330 // boundary
32331 const unsigned n_polyline = tmp_polygon_pt->npolyline();
32332 // Loop over the polylines
32333 for (unsigned p = 0; p < n_polyline; p++)
32334 {
32335 // Get a temporary representation of the polyline
32336 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
32337
32338 // Is the initial vertex connected?
32339 if (tmp_polyline_pt->is_initial_vertex_connected())
32340 {
32341 // Get the boundary id of the current polyline
32342 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32343
32344 // Boundary id to which the curve is connected
32345 const unsigned dst_bnd_id =
32346 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32347
32348 // Boundary chunk to which the curve is connected
32349 const unsigned dst_chunk =
32350 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32351
32352 // Get the nodes representation of the current boundary
32353 Vector<Vector<Node*>> src_bnd_node_pt =
32354 bnd_sorted_segment_node_pt[bnd_id];
32355
32356 // Get the nodes representation of the boundary to connect
32357 Vector<Vector<Node*>> dst_bnd_node_pt =
32358 bnd_sorted_segment_node_pt[dst_bnd_id];
32359
32360 // Add the repeated node to the list of non delete-able
32361 // vertices
32362 add_non_delete_vertices_from_boundary_helper(
32363 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32364
32365 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32366
32367 // Is the final vertex connected?
32368 if (tmp_polyline_pt->is_final_vertex_connected())
32369 {
32370 // Get the boundary id of the current polyline
32371 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32372
32373 // Boundary id to which the curve is connected
32374 const unsigned dst_bnd_id =
32375 tmp_polyline_pt->final_vertex_connected_bnd_id();
32376
32377 // Boundary chunk to which the curve is connected
32378 const unsigned dst_chunk =
32379 tmp_polyline_pt->final_vertex_connected_n_chunk();
32380
32381 // Get the nodes representation of the current boundary
32382 Vector<Vector<Node*>> src_bnd_node_pt =
32383 bnd_sorted_segment_node_pt[bnd_id];
32384
32385 // Get the nodes representation of the boundary to connect
32386 Vector<Vector<Node*>> dst_bnd_node_pt =
32387 bnd_sorted_segment_node_pt[dst_bnd_id];
32388
32389 // Add the repeated node to the list of non delete-able
32390 // vertices
32391 add_non_delete_vertices_from_boundary_helper(
32392 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32393
32394 } // if (tmp_polyline_pt->is_final_vertex_connected())
32395
32396 } // for (p < n_polyline)
32397
32398 } // for (i < n_internal_boundaries)
32399
32400 // ------------------------------------------------------------------
32401 // Open boundaries (nonclosed internal boundaries)
32402 // ------------------------------------------------------------------
32403 // Loop over the internal open boundaries
32404 for (unsigned i = 0; i < n_open_boundaries; i++)
32405 {
32406 // Get a temporary representation for the open curve
32407 TriangleMeshOpenCurve* tmp_open_curve_pt =
32408 this->Internal_open_curve_pt[i];
32409
32410 // Get the number of curve sections associated to the current
32411 // internal open boundary
32412 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
32413
32414 // Loop over the curve section
32415 for (unsigned p = 0; p < n_curve_section; p++)
32416 {
32417 // Get a temporary representation of the curve section
32418 // (polyline)
32419 TriangleMeshPolyLine* tmp_polyline_pt =
32420 tmp_open_curve_pt->polyline_pt(p);
32421
32422 // Is the initial vertex connected?
32423 if (tmp_polyline_pt->is_initial_vertex_connected())
32424 {
32425 // Get the boundary id of the current polyline
32426 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32427
32428 // Boundary id to which the curve is connected
32429 const unsigned dst_bnd_id =
32430 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32431
32432 // Boundary chunk to which the curve is connected
32433 const unsigned dst_chunk =
32434 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32435
32436 // Get the nodes representation of the current boundary
32437 Vector<Vector<Node*>> src_bnd_node_pt =
32438 bnd_sorted_segment_node_pt[bnd_id];
32439
32440 // Get the nodes representation of the boundary to connect
32441 Vector<Vector<Node*>> dst_bnd_node_pt =
32442 bnd_sorted_segment_node_pt[dst_bnd_id];
32443
32444 // Add the repeated node to the list of non delete-able
32445 // vertices
32446 add_non_delete_vertices_from_boundary_helper(
32447 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32448
32449 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32450
32451 // Is the final vertex connected?
32452 if (tmp_polyline_pt->is_final_vertex_connected())
32453 {
32454 // Get the boundary id of the current polyline
32455 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32456
32457 // Boundary id to which the curve is connected
32458 const unsigned dst_bnd_id =
32459 tmp_polyline_pt->final_vertex_connected_bnd_id();
32460
32461 // Boundary chunk to which the curve is connected
32462 const unsigned dst_chunk =
32463 tmp_polyline_pt->final_vertex_connected_n_chunk();
32464
32465 // Get the nodes representation of the current boundary
32466 Vector<Vector<Node*>> src_bnd_node_pt =
32467 bnd_sorted_segment_node_pt[bnd_id];
32468
32469 // Get the nodes representation of the boundary to connect
32470 Vector<Vector<Node*>> dst_bnd_node_pt =
32471 bnd_sorted_segment_node_pt[dst_bnd_id];
32472
32473 // Add the repeated node to the list of non delete-able
32474 // vertices
32475 add_non_delete_vertices_from_boundary_helper(
32476 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32477
32478 } // if (tmp_polyline_pt->is_final_vertex_connected())
32479
32480 } // for (p < n_curve_section)
32481
32482 } // for (i < n_open_boundaries)
32483
32484#ifdef OOMPH_HAS_MPI
32485 // ------------------------------------------------------------------
32486 // Shared boundaries (only for distributed meshes)
32487 // ------------------------------------------------------------------
32488
32489 // Check if we need to include any information associated with
32490 // shared boundaries
32491 if (this->is_mesh_distributed())
32492 {
32493 // Get the rank of the current processor
32494 const unsigned my_rank = this->communicator_pt()->my_rank();
32495
32496 // Get the number of shared curves in the current processor
32497 const unsigned n_shared_curves = this->nshared_boundary_curves(my_rank);
32498
32499 // Loop over the shared curves
32500 for (unsigned i = 0; i < n_shared_curves; i++)
32501 {
32502 // Get the number of polylines associated to the current shared
32503 // curve
32504 const unsigned n_polyline = this->nshared_boundary_polyline(my_rank, i);
32505
32506 // Loop over the polylines associated to the current shared
32507 // curve
32508 for (unsigned p = 0; p < n_polyline; p++)
32509 {
32510 // Get a temporary representation of the shared polyline
32511 TriangleMeshPolyLine* tmp_polyline_pt =
32512 this->shared_boundary_polyline_pt(my_rank, i, p);
32513
32514 // Is the initial vertex connected?
32515 if (tmp_polyline_pt->is_initial_vertex_connected())
32516 {
32517 // Get the boundary id of the current polyline
32518 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32519
32520 // Boundary id to which the curve is connected
32521 const unsigned dst_bnd_id =
32522 tmp_polyline_pt->initial_vertex_connected_bnd_id();
32523
32524 // Boundary chunk to which the curve is connected
32525 const unsigned dst_chunk =
32526 tmp_polyline_pt->initial_vertex_connected_n_chunk();
32527
32528 // Get the nodes representation of the current boundary
32529 Vector<Vector<Node*>> src_bnd_node_pt =
32530 bnd_sorted_segment_node_pt[bnd_id];
32531
32532 // Get the nodes representation of the boundary to connect
32533 Vector<Vector<Node*>> dst_bnd_node_pt =
32534 bnd_sorted_segment_node_pt[dst_bnd_id];
32535
32536 // Add the repeated node to the list of non delete-able
32537 // vertices
32538 add_non_delete_vertices_from_boundary_helper(
32539 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32540
32541 } // if (tmp_polyline_pt->is_initial_vertex_connected())
32542
32543 // Is the final vertex connected?
32544 if (tmp_polyline_pt->is_final_vertex_connected())
32545 {
32546 // Get the boundary id of the current polyline
32547 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
32548
32549 // Boundary id to which the curve is connected
32550 const unsigned dst_bnd_id =
32551 tmp_polyline_pt->final_vertex_connected_bnd_id();
32552
32553 // Boundary chunk to which the curve is connected
32554 const unsigned dst_chunk =
32555 tmp_polyline_pt->final_vertex_connected_n_chunk();
32556
32557 // Get the nodes representation of the current boundary
32558 Vector<Vector<Node*>> src_bnd_node_pt =
32559 bnd_sorted_segment_node_pt[bnd_id];
32560
32561 // Get the nodes representation of the boundary to connect
32562 Vector<Vector<Node*>> dst_bnd_node_pt =
32563 bnd_sorted_segment_node_pt[dst_bnd_id];
32564
32565 // Add the repeated node to the list of non delete-able
32566 // vertices
32567 add_non_delete_vertices_from_boundary_helper(
32568 src_bnd_node_pt, dst_bnd_node_pt, dst_bnd_id, dst_chunk);
32569
32570 } // if (tmp_polyline_pt->is_final_vertex_connected())
32571
32572 } // for (p < n_polyline)
32573
32574 } // for (i < n_shared_curves)
32575
32576 } // if (this->is_mesh_distributed())
32577
32578#endif // #ifdef OOMPH_HAS_MPI
32579 }
32580
32581 //=========================================================================
32582 /// Adds the vertices from the sources boundary that are
32583 /// repeated in the destination boundary to the list of non
32584 /// delete-able vertices in the destination boundary
32585 //=========================================================================
32586 template<class ELEMENT>
32589 Vector<Vector<Node*>> src_bound_segment_node_pt,
32590 Vector<Vector<Node*>> dst_bound_segment_node_pt,
32591 const unsigned& dst_bnd_id,
32592 const unsigned& dst_bnd_chunk)
32593 {
32594 // Get the number of segments in the source boundary
32595 const unsigned n_seg = src_bound_segment_node_pt.size();
32596 // Loop over the segments in the source boundary
32597 for (unsigned iseg = 0; iseg < n_seg; iseg++)
32598 {
32599 // Get the number of nodes in the current segment
32600 const unsigned nnode = src_bound_segment_node_pt[iseg].size();
32601 // Get the left and right node of the current segment
32602 Node* left_node_pt = src_bound_segment_node_pt[iseg][0];
32603 Node* right_node_pt = src_bound_segment_node_pt[iseg][nnode - 1];
32604
32605 // Get the number of segments in the destination boundary
32606 const unsigned n_dst_seg = dst_bound_segment_node_pt.size();
32607 // Loop over the segments in the destination boundary
32608 for (unsigned jseg = 0; jseg < n_dst_seg; jseg++)
32609 {
32610 // Get the number of nodes on the current destination segment
32611 const unsigned n_dst_node = dst_bound_segment_node_pt[jseg].size();
32612 // Loop over the nodes until the node has been found or we have
32613 // visited all the nodes
32614 for (unsigned jnode = 0; jnode < n_dst_node; jnode++)
32615 {
32616 // Get a pointer to the jnode in the destination segment
32617 // boundary
32618 Node* tmp_node_pt = dst_bound_segment_node_pt[jseg][jnode];
32619 // Is the node the same as the left or right node if
32620 // the source segment boundary
32621 if (tmp_node_pt == left_node_pt)
32622 {
32623 // We have foud the node to connect, get the vertex of the node
32624 Vector<double> vertex(2);
32625 vertex[0] = tmp_node_pt->x(0);
32626 vertex[1] = tmp_node_pt->x(1);
32627
32628 // Establish the vertex coordinate as untouchable in the
32629 // destination boundary during the adaptation process. It
32630 // means that unrefinement can not take off the vertices
32631 // that receive connections in the destination boundary
32632 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32633 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32634 // insert(vertex);
32635
32636 // return
32637 return;
32638
32639 } // if (tmp_node_pt == left_node_pt)
32640 else if (tmp_node_pt == right_node_pt)
32641 {
32642 // We have foud the node to connect, get the vertex of the node
32643 Vector<double> vertex(2);
32644 vertex[0] = tmp_node_pt->x(0);
32645 vertex[1] = tmp_node_pt->x(1);
32646
32647 // Establish the vertex coordinate as untouchable in the
32648 // destination boundary during the adaptation process. It
32649 // means that unrefinement can not take off the vertices
32650 // that receive connections in the destination boundary
32651 // Boundary_chunk_connections_pt[dst_bnd_id][dst_bnd_chunk].
32652 // insert(vertex);
32653 Boundary_connections_pt[dst_bnd_id].insert(vertex);
32654
32655 // return
32656 return;
32657
32658 } // else if (tmp_node_pt == right_node_pt)
32659
32660 } // for (jnode < n_dst_node)
32661
32662 } // for (jseg < n_dst_seg)
32663
32664 } // for (iseg < n_seg)
32665 }
32666
32667#ifdef OOMPH_HAS_MPI
32668 //=========================================================================
32669 /// Synchronise the vertices that are marked for non deletion
32670 // on the shared boundaries. Unrefinement of shared boundaries is
32671 // performed only if the candidate node is not marked for non deletion
32672 //=========================================================================
32673 template<class ELEMENT>
32674 const void RefineableTriangleMesh<
32675 ELEMENT>::synchronize_shared_boundary_connections()
32676 {
32677 // Get the number of processors
32678 const unsigned nproc = this->communicator_pt()->nproc();
32679 // Get my rank
32680 const unsigned my_rank = this->communicator_pt()->my_rank();
32681
32682 // loop over the processors
32683 for (unsigned jproc = 0; jproc < nproc; jproc++)
32684 {
32685 // The number of boundaries shared with the current processor
32686 // (if jproc==my_rank then there are no shared boundaries
32687 // between them)
32688 const unsigned n_shd_bnd_jproc = this->nshared_boundaries(my_rank, jproc);
32689
32690 // Are there shared boundaries with the jproc processor?
32691 // There are no info. with myself
32692 if (jproc != my_rank && n_shd_bnd_jproc > 0)
32693 {
32694 // Storage for the boundaries ids with vertices for non
32695 // deletion
32696 Vector<unsigned> shd_bnd_id_for_non_deletion;
32697
32698 // Storage for chunk numbers of boundaries with vertices
32699 // for non deletion
32700 Vector<unsigned> chunk_for_non_deletion;
32701
32702 // The number of vertices for nondeletion in the shared
32703 // boundaries
32704 Vector<unsigned> number_vertices_non_deletion;
32705
32706 // Vertices marked for nondeletion in shared boundaries
32707 Vector<Vector<Vector<double>>> vertices_for_non_deletion;
32708
32709 // Get the boundary ids of the shared boundaries with jproc
32710 // processor
32711 Vector<unsigned> shd_bnd_ids =
32712 this->shared_boundaries_ids(my_rank, jproc);
32713
32714 // Get the number of shared boundaries with jproc
32715 const unsigned n_shd_bnd_jproc = shd_bnd_ids.size();
32716 // loop over the shared boundaries with jproc
32717 for (unsigned ishd_bnd = 0; ishd_bnd < n_shd_bnd_jproc; ishd_bnd++)
32718 {
32719 // Get the shared boudary id
32720 const unsigned shd_bnd_id = shd_bnd_ids[ishd_bnd];
32721 // Get the associated polyline
32722 TriangleMeshPolyLine* shd_polyline_pt =
32723 this->boundary_polyline_pt(shd_bnd_id);
32724 // Get the chunk number
32725 const unsigned chunk = shd_polyline_pt->boundary_chunk();
32726
32727 // Store the vertices not allowed for deletion
32728 std::set<Vector<double>> no_delete_vertex;
32729
32730 // Does the boundary has vertives for nondeleteion?
32731 const bool boundary_receive_connections =
32732 this->boundary_connections(shd_bnd_id, chunk, no_delete_vertex);
32733
32734 // Get the number of vertices for nondeletion
32735 const unsigned n_non_delete_vertex = no_delete_vertex.size();
32736
32737 // Are there vertices for nondeletion?
32738 if (boundary_receive_connections && n_non_delete_vertex > 0)
32739 {
32740 // Add the shared boundary id
32741 shd_bnd_id_for_non_deletion.push_back(shd_bnd_id);
32742 // Add the chunk number
32743 chunk_for_non_deletion.push_back(chunk);
32744 // Add the number of vertices for non deletion
32745 number_vertices_non_deletion.push_back(n_non_delete_vertex);
32746
32747 // The list of vertices to add
32748 Vector<Vector<double>> tmp_vertices;
32749
32750 // Add the vertices for non deletion
32751 for (std::set<Vector<double>>::iterator it =
32752 no_delete_vertex.begin();
32753 it != no_delete_vertex.end();
32754 it++)
32755 {
32756 // Get the vertex coordinate
32757 Vector<double> vertex = (*it);
32758 tmp_vertices.push_back(vertex);
32759 }
32760
32761 // Add the vertices coordinates to a vector storage
32762 vertices_for_non_deletion.push_back(tmp_vertices);
32763
32764 } // if (boundary_receive_connections && n_non_delete_vertex > 0)
32765
32766 } // for (ishd_bnd<n_shd_bnd_jproc)
32767
32768 // ----------------------------------------------------------
32769 // ----------------------------------------------------------
32770 // ----------------------------------------------------------
32771 // Now send the info. to the other processor (jproc)
32772 // ----------------------------------------------------------
32773 // ----------------------------------------------------------
32774 // ----------------------------------------------------------
32775 // Get the communicator of the mesh
32776 OomphCommunicator* comm_pt = this->communicator_pt();
32777
32778 // Set MPI info
32779 MPI_Status status;
32780 MPI_Request request;
32781
32782 // -----------------------------------------------------------
32783 // Prepare the data
32784 // Get the number of shared boundaires with vertices marked
32785 // for non deletion
32786 const unsigned n_shd_bnd_with_non_delete_vertices =
32787 shd_bnd_id_for_non_deletion.size();
32788
32789 // Size of the package
32790 const unsigned size_package = 3;
32791 // Ndata to send
32792 const unsigned n_unsigned_data_to_send =
32793 n_shd_bnd_with_non_delete_vertices * size_package;
32794 // The flat package to send the info.
32795 Vector<unsigned> flat_package_unsigned_send(n_unsigned_data_to_send);
32796 Vector<double> flat_package_double_send;
32797
32798 Vector<unsigned> flat_package_unsigned_recv;
32799 Vector<double> flat_package_double_recv;
32800
32801 // Prepare the data to be sent
32802 unsigned j = 0;
32803 for (unsigned i = 0; i < n_shd_bnd_with_non_delete_vertices; i++)
32804 {
32805 // The shared boundary id
32806 flat_package_unsigned_send[j++] = shd_bnd_id_for_non_deletion[i];
32807 // The chunk number
32808 flat_package_unsigned_send[j++] = chunk_for_non_deletion[i];
32809 // The number of vertices for nondeletion
32810 flat_package_unsigned_send[j++] = number_vertices_non_deletion[i];
32811 // Also package the vertices
32812 const unsigned n_vertices_non_deletion =
32813 number_vertices_non_deletion[i];
32814 // Loop over the vertices and store them in the flat
32815 // package to be sent
32816 for (unsigned h = 0; h < n_vertices_non_deletion; h++)
32817 {
32818 flat_package_double_send.push_back(
32819 vertices_for_non_deletion[i][h][0]);
32820 flat_package_double_send.push_back(
32821 vertices_for_non_deletion[i][h][1]);
32822 } // for (h<n_vertices_non_deletion)
32823
32824 } // for (i<n_shd_bnd_with_non_delete_vertices)
32825
32826 // ----------------------------------------------------------
32827 int send_proc = jproc;
32828 int recv_proc = jproc;
32829 unsigned send_count_unsigned_values = n_unsigned_data_to_send;
32830 unsigned send_count_double_values = flat_package_double_send.size();
32831 //-----------------------------------------------------------
32832 // Do the transfering of info.
32833 //-----------------------------------------------------------
32834 // Start with UNSIGNED info.
32835 MPI_Isend(&send_count_unsigned_values,
32836 1,
32837 MPI_UNSIGNED,
32838 send_proc,
32839 1,
32840 comm_pt->mpi_comm(),
32841 &request);
32842
32843 unsigned receive_count_unsigned_values = 0;
32844 MPI_Recv(&receive_count_unsigned_values,
32845 1,
32846 MPI_UNSIGNED,
32847 recv_proc,
32848 1,
32849 comm_pt->mpi_comm(),
32850 &status);
32851
32852 MPI_Wait(&request, MPI_STATUS_IGNORE);
32853
32854 // Send the actual data
32855 if (send_count_unsigned_values != 0)
32856 {
32857 MPI_Isend(&flat_package_unsigned_send[0],
32858 send_count_unsigned_values,
32859 MPI_UNSIGNED,
32860 send_proc,
32861 2,
32862 comm_pt->mpi_comm(),
32863 &request);
32864 }
32865
32866 // Receive the actual data
32867 if (receive_count_unsigned_values != 0)
32868 {
32869 flat_package_unsigned_recv.resize(receive_count_unsigned_values);
32870 MPI_Recv(&flat_package_unsigned_recv[0],
32871 receive_count_unsigned_values,
32872 MPI_UNSIGNED,
32873 recv_proc,
32874 2,
32875 comm_pt->mpi_comm(),
32876 &status);
32877 }
32878
32879 // Wait for sending the data and the other processor
32880 // receives
32881 if (send_count_unsigned_values != 0)
32882 {
32883 MPI_Wait(&request, MPI_STATUS_IGNORE);
32884 }
32885
32886 //-----------------------------------------------------------
32887 // Then continue with DOUBLE info.
32888 MPI_Isend(&send_count_double_values,
32889 1,
32890 MPI_UNSIGNED,
32891 send_proc,
32892 1,
32893 comm_pt->mpi_comm(),
32894 &request);
32895
32896 unsigned receive_count_double_values = 0;
32897 MPI_Recv(&receive_count_double_values,
32898 1,
32899 MPI_UNSIGNED,
32900 recv_proc,
32901 1,
32902 comm_pt->mpi_comm(),
32903 &status);
32904
32905 MPI_Wait(&request, MPI_STATUS_IGNORE);
32906
32907 // Send the actual data
32908 if (send_count_double_values != 0)
32909 {
32910 MPI_Isend(&flat_package_double_send[0],
32911 send_count_double_values,
32912 MPI_DOUBLE,
32913 send_proc,
32914 2,
32915 comm_pt->mpi_comm(),
32916 &request);
32917 }
32918
32919 // Receive the actual data
32920 if (receive_count_double_values != 0)
32921 {
32922 flat_package_double_recv.resize(receive_count_double_values);
32923 MPI_Recv(&flat_package_double_recv[0],
32924 receive_count_double_values,
32925 MPI_DOUBLE,
32926 recv_proc,
32927 2,
32928 comm_pt->mpi_comm(),
32929 &status);
32930 }
32931
32932 // Wait for sending the data and the other processor
32933 // receives
32934 if (send_count_double_values != 0)
32935 {
32936 MPI_Wait(&request, MPI_STATUS_IGNORE);
32937 }
32938
32939 // ------------------------------------------------------------
32940 // ------------------------------------------------------------
32941 // ------------------------------------------------------------
32942 // Now unpackage the data
32943 // ------------------------------------------------------------
32944 // ------------------------------------------------------------
32945 // ------------------------------------------------------------
32946
32947 // Storage for the boundaries ids with vertices for non
32948 // deletion
32949 Vector<unsigned> recv_shd_bnd_id_for_non_deletion;
32950
32951 // Storage for chunk numbers of boundaries with vertices
32952 // for non deletion
32953 Vector<unsigned> recv_chunk_for_non_deletion;
32954
32955 // The number of vertices for nondeletion in the shared
32956 // boundaries
32957 Vector<unsigned> recv_number_vertices_non_deletion;
32958
32959 // Vertices marked for nondeletion in shared boundaries
32960 Vector<Vector<Vector<double>>> recv_vertices_for_non_deletion;
32961
32962 // Counter
32963 j = 0;
32964 for (unsigned i = 0; i < receive_count_unsigned_values; i += 3)
32965 {
32966 // Get the shared boundary id
32967 const unsigned recv_shd_bnd_id = flat_package_unsigned_recv[i];
32968 recv_shd_bnd_id_for_non_deletion.push_back(recv_shd_bnd_id);
32969 // Get the chunk number
32970 const unsigned recv_chunk = flat_package_unsigned_recv[i + 1];
32971 recv_chunk_for_non_deletion.push_back(recv_chunk);
32972 // Get the number of vertices for non deletion
32973 const unsigned recv_num_vertices = flat_package_unsigned_recv[i + 2];
32974 recv_number_vertices_non_deletion.push_back(recv_num_vertices);
32975
32976 // Create a temporal storage
32977 Vector<Vector<double>> temp_recv_vertices;
32978 // Now get the vertices
32979 for (unsigned h = 0; h < recv_num_vertices; h++)
32980 {
32981 Vector<double> tmp_vertex(2);
32982 tmp_vertex[0] = flat_package_double_recv[j++];
32983 tmp_vertex[1] = flat_package_double_recv[j++];
32984 // Add the vertex to the vector of vertices
32985 temp_recv_vertices.push_back(tmp_vertex);
32986 } // for (h<recv_num_vertices)
32987
32988 // Add the vertices to the vector of vertices
32989 recv_vertices_for_non_deletion.push_back(temp_recv_vertices);
32990
32991 } // for(i<receive_count_unsigned_values)
32992
32993 // ---------------------------------------------------------
32994 // ---------------------------------------------------------
32995 // ---------------------------------------------------------
32996 // Now add the vertices to the data structures to mark them
32997 // as non delete-able
32998 // ---------------------------------------------------------
32999 // ---------------------------------------------------------
33000 // ---------------------------------------------------------
33001
33002 // Get the number of shd boundaries that have vertices
33003 // marked for non deletion
33004 const unsigned n_recv_shd_bnd_id_for_non_deletion =
33005 recv_shd_bnd_id_for_non_deletion.size();
33006 // loop over the shared boundaries and add the data for non
33007 // deletion
33008 for (unsigned i = 0; i < n_recv_shd_bnd_id_for_non_deletion; i++)
33009 {
33010 // Get the shared boundary id.
33011 const unsigned shd_bnd_id = recv_shd_bnd_id_for_non_deletion[i];
33012 // Get the chunk number
33013 unsigned chunk = recv_chunk_for_non_deletion[i];
33014 // Increase and decrease the chunk number to avoid the
33015 // warning when compiling without PARANOID
33016 chunk++;
33017 chunk--;
33018
33019 // Get the number of vertices marked for non deletion
33020 const unsigned n_vertices = recv_number_vertices_non_deletion[i];
33021 // Add all the vertices
33022 for (unsigned h = 0; h < n_vertices; h++)
33023 {
33024 // Get the vertex
33025 Vector<double> vertex(2);
33026 vertex[0] = recv_vertices_for_non_deletion[i][h][0];
33027 vertex[1] = recv_vertices_for_non_deletion[i][h][1];
33028 // Add the vertex to the data structure for non
33029 // deletion
33030 // Boundary_chunk_connections_pt[shd_bnd_id][chunk].
33031 // insert(vertex);
33032 Boundary_connections_pt[shd_bnd_id].insert(vertex);
33033
33034 } // for (h<n_vertices)
33035
33036 } // for (i<n_recv_shd_bnd_id_for_non_deletion)
33037
33038 } // if (jproc != my_rank && n_shd_bnd_jproc > 0)
33039
33040 } // for (jproc < nproc)
33041 }
33042#endif // #ifdef OOMPH_HAS_MPI
33043
33044 //=========================================================================
33045 /// After unrefinement and refinement has taken place compute
33046 /// the new vertices numbers of the temporary representation of the
33047 // boundaries to connect.
33048 //=========================================================================
33049 template<class ELEMENT>
33051 Vector<TriangleMeshPolygon*>& tmp_outer_polygons_pt,
33052 Vector<TriangleMeshOpenCurve*>& tmp_open_curves_pt)
33053 {
33054 // Dummy storages
33055 Vector<TriangleMeshPolyLine*> dummy_resume_initial_connection_polyline_pt;
33056 Vector<TriangleMeshPolyLine*> dummy_resume_final_connection_polyline_pt;
33057
33058 // Clear the storage
33059 dummy_resume_initial_connection_polyline_pt.clear();
33060 dummy_resume_final_connection_polyline_pt.clear();
33061
33062 // Get the initial shared boundary id (to check whether the
33063 // polylines represent original or shared boundaries)
33064 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33065
33066 // ------------------------------------------------------------------
33067 // This seems unnecesary since the outer polygons does not create
33068 // connections with other boundaries (the original ones)
33069 // ------------------------------------------------------------------
33070 // Unnecessary?
33071 // ------------------------------------------------------------------
33072
33073 // Loop over the temporary outer polygons create the connection
33074 // information of those boundaries marked to be connected at their
33075 // ends
33076
33077 // ------------------------------------------------------------------
33078 // Temporary outer polygons
33079 // ------------------------------------------------------------------
33080
33081 // Get the number of outer boundaries (closed boundaries)
33082 const unsigned n_outer_boundaries = tmp_outer_polygons_pt.size();
33083
33084 // Loop over the outer boundaries
33085 for (unsigned i = 0; i < n_outer_boundaries; i++)
33086 {
33087 // Get a temporary polygon representation
33088 TriangleMeshPolygon* tmp_polygon_pt = tmp_outer_polygons_pt[i];
33089 // Get the number of polylines associated to the current outer
33090 // boundary
33091 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33092 // Loop over the polylines
33093 for (unsigned p = 0; p < n_polyline; p++)
33094 {
33095 // Get a temporary representation of the polyline
33096 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33097
33098 // Get the boundary id
33099 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33100
33101 // Is the boundary to connect a shared boundary
33102 if (bnd_id < init_shd_bnd_id)
33103 {
33104 // Restore the connections of the current polyline
33105 restore_polyline_connections_helper(
33106 tmp_polyline_pt,
33107 dummy_resume_initial_connection_polyline_pt,
33108 dummy_resume_final_connection_polyline_pt);
33109
33110 } // if (bnd_id < init_shd_bnd_id)
33111
33112 } // for (p < n_polyline)
33113
33114 } // for (i < n_outer_boundaries)
33115
33116 // ------------------------------------------------------------------
33117 // Unnecessary?
33118 // ------------------------------------------------------------------
33119
33120 // ------------------------------------------------------------------
33121 // Temporary open boundaries (nonclosed internal boundaries)
33122 // ------------------------------------------------------------------
33123
33124 // Get the number of internal boundaries (open boundaries)
33125 const unsigned n_open_boundaries = tmp_open_curves_pt.size();
33126
33127 // Loop over the internal open boundaries
33128 for (unsigned i = 0; i < n_open_boundaries; i++)
33129 {
33130 // Get a temporary representation for the open curve
33131 TriangleMeshOpenCurve* tmp_open_curve_pt = tmp_open_curves_pt[i];
33132
33133 // Get the number of curve sections associated to the current
33134 // internal open boundary
33135 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33136
33137 // Loop over the curve section
33138 for (unsigned p = 0; p < n_curve_section; p++)
33139 {
33140 // Get a temporary representation of the curve section
33141 // (polyline)
33142 TriangleMeshPolyLine* tmp_polyline_pt =
33143 tmp_open_curve_pt->polyline_pt(p);
33144
33145 // Get the boundary id
33146 const unsigned bnd_id = tmp_polyline_pt->boundary_id();
33147
33148 // Is the boundary to connect a shared boundary
33149 if (bnd_id < init_shd_bnd_id)
33150 {
33151 // Restore the connections of the current polyline
33152 restore_polyline_connections_helper(
33153 tmp_polyline_pt,
33154 dummy_resume_initial_connection_polyline_pt,
33155 dummy_resume_final_connection_polyline_pt);
33156
33157 } // if (bnd_id < init_shd_bnd_id)
33158
33159 } // for (p < n_curve_section)
33160
33161 } // for (i < n_open_boundaries)
33162 }
33163
33164 //=========================================================================
33165 /// After unrefinement and refinement has taken place compute
33166 /// the new vertices numbers of the boundaries to connect (in a
33167 /// distributed scheme it may be possible that the destination
33168 /// boundary does no longer exist, therefore the connection is
33169 /// suspended. It is not permanently deleted because if load balance
33170 /// takes place it may be possible that the boundary to connect be
33171 /// part of the new domain representation, so the connection would
33172 /// exist)
33173 //=========================================================================
33174 template<class ELEMENT>
33176 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
33177 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
33178 {
33179 // Clear the storage
33180 resume_initial_connection_polyline_pt.clear();
33181 resume_final_connection_polyline_pt.clear();
33182
33183 // Loop over the boundaries in the domain (outer, internal -- closed
33184 // and open) and restore the connection information of those
33185 // boundaries marked to be connected at their ends
33186
33187 // ------------------------------------------------------------------
33188 // Outer boundaries
33189 // ------------------------------------------------------------------
33190
33191 // Get the number of outer boundaries (closed boundaries)
33192 const unsigned n_outer_boundaries = this->Outer_boundary_pt.size();
33193
33194 // Loop over the outer boundaries
33195 for (unsigned i = 0; i < n_outer_boundaries; i++)
33196 {
33197 // Get a temporary polygon representation
33198 TriangleMeshPolygon* tmp_polygon_pt = this->Outer_boundary_pt[i];
33199 // Get the number of polylines associated to the current outer
33200 // boundary
33201 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33202 // Loop over the polylines
33203 for (unsigned p = 0; p < n_polyline; p++)
33204 {
33205 // Get a temporary representation of the polyline
33206 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33207
33208 // Restore the connections of the current polyline
33209 restore_polyline_connections_helper(
33210 tmp_polyline_pt,
33211 resume_initial_connection_polyline_pt,
33212 resume_final_connection_polyline_pt);
33213
33214 } // for (p < n_polyline)
33215
33216 } // for (i < n_outer_boundaries)
33217
33218 // ------------------------------------------------------------------
33219 // Internal boundaries
33220 // ------------------------------------------------------------------
33221
33222 // Get the number of internal boundaries (closed boundaries)
33223 const unsigned n_internal_boundaries = this->Internal_polygon_pt.size();
33224
33225 // Loop over the internal boundaries
33226 for (unsigned i = 0; i < n_internal_boundaries; i++)
33227 {
33228 // Get a temporary polygon representation
33229 TriangleMeshPolygon* tmp_polygon_pt = this->Internal_polygon_pt[i];
33230 // Get the number of polylines associated to the current internal
33231 // boundary
33232 const unsigned n_polyline = tmp_polygon_pt->npolyline();
33233 // Loop over the polylines
33234 for (unsigned p = 0; p < n_polyline; p++)
33235 {
33236 // Get a temporary representation of the polyline
33237 TriangleMeshPolyLine* tmp_polyline_pt = tmp_polygon_pt->polyline_pt(p);
33238
33239 // Restore the connections of the current polyline
33240 restore_polyline_connections_helper(
33241 tmp_polyline_pt,
33242 resume_initial_connection_polyline_pt,
33243 resume_final_connection_polyline_pt);
33244
33245 } // for (p < n_polyline)
33246
33247 } // for (i < n_internal_boundaries)
33248
33249 // ------------------------------------------------------------------
33250 // Open boundaries (nonclosed internal boundaries)
33251 // ------------------------------------------------------------------
33252
33253 // Get the number of internal boundaries (open boundaries)
33254 const unsigned n_open_boundaries = this->Internal_open_curve_pt.size();
33255
33256 // Loop over the internal open boundaries
33257 for (unsigned i = 0; i < n_open_boundaries; i++)
33258 {
33259 // Get a temporary representation for the open curve
33260 TriangleMeshOpenCurve* tmp_open_curve_pt =
33261 this->Internal_open_curve_pt[i];
33262
33263 // Get the number of curve sections associated to the current
33264 // internal open boundary
33265 const unsigned n_curve_section = tmp_open_curve_pt->ncurve_section();
33266
33267 // Loop over the curve section
33268 for (unsigned p = 0; p < n_curve_section; p++)
33269 {
33270 // Get a temporary representation of the curve section
33271 // (polyline)
33272 TriangleMeshPolyLine* tmp_polyline_pt =
33273 tmp_open_curve_pt->polyline_pt(p);
33274
33275 // Restore the connections of the current polyline
33276 restore_polyline_connections_helper(
33277 tmp_polyline_pt,
33278 resume_initial_connection_polyline_pt,
33279 resume_final_connection_polyline_pt);
33280
33281 } // for (p < n_curve_section)
33282
33283 } // for (i < n_open_boundaries)
33284 }
33285
33286 //=========================================================================
33287 /// Restore the connections of the specific polyline
33288 /// The vertices numbering on the destination boundaries may have
33289 /// change because of (un)refinement in the destination boundaries.
33290 /// Also deals with connection that do not longer exist because the
33291 /// destination boundary does no longer exist because of the distribution
33292 /// process
33293 //=========================================================================
33294 template<class ELEMENT>
33296 TriangleMeshPolyLine* polyline_pt,
33297 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
33298 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
33299 {
33300 // If the polyline is connected at any of its ends compute the new
33301 // vertex number on the destination boundary
33302
33303 // ------------------------------------------------------------------
33304 // Is the initial vertex connected?
33305 if (polyline_pt->is_initial_vertex_connected())
33306 {
33307 // The pointer to the boundary to connect
33308 TriangleMeshPolyLine* poly_to_connect_pt = 0;
33309
33310 // Get the boundary id of the destination/connected boundary
33311 const unsigned dst_bnd_id_initial =
33312 polyline_pt->initial_vertex_connected_bnd_id();
33313
33314 // Get the initial vertex on the current boundary
33315 Vector<double> src_vertex_coordinates_initial =
33316 polyline_pt->vertex_coordinate(0);
33317
33318#ifdef PARANOID
33319 // Is the mesh distributed?
33320#ifdef OOMPH_HAS_MPI
33321 if (this->is_mesh_distributed())
33322 {
33323 // Get the initial shared boundary id
33324 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33325 // Is the boundary to connect a shared boundary
33326 if (dst_bnd_id_initial >= init_shd_bnd_id)
33327 {
33328 // Get the current polyline original boundary id
33329 const unsigned bnd_id = polyline_pt->boundary_id();
33330 std::ostringstream error_message;
33331 error_message
33332 << "INITIAL VERTEX CONNECTION\n"
33333 << "The current original boundary is trying to connect to a\n"
33334 << "shared boundary, this is not allowed. In this case the\n"
33335 << "shared boundary should be the one that connects with the\n"
33336 << "original boundary\n"
33337 << "The current original boundary (" << bnd_id << ") is marked\n"
33338 << "to have a connection at the\nINITIAL vertex ("
33339 << src_vertex_coordinates_initial[0] << ","
33340 << src_vertex_coordinates_initial[1] << ")\n"
33341 << "with the shared boundary (" << dst_bnd_id_initial << ")\n"
33342 << "This is the list of vertices on the shared destination "
33343 "boundary\n";
33344 // Get the pointer to the associated polyline by using the
33345 // boundary id
33346 TriangleMeshPolyLine* dst_polyline =
33347 this->boundary_polyline_pt(dst_bnd_id_initial);
33348 // The number of vertices on the destination boundary
33349 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33350 // Loop over the vertices print them
33351 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33352 {
33353 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33354 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33355 << current_vertex[1] << ")\n";
33356 }
33357 throw OomphLibError(
33358 error_message.str(),
33359 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33360 OOMPH_EXCEPTION_LOCATION);
33361 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33362
33363 } // if (this->is_mesh_distributed())
33364#endif // #ifdef OOMPH_HAS_MPI
33365
33366#endif // #ifdef PARANOID
33367
33368 // Flag to indicate if the vertex was found on the destination
33369 // boundary
33370 bool found_vertex_on_dst_boundary_initial = false;
33371
33372 // Flag that stores the chunk number to connect (only used in
33373 // distributed meshes)
33374 unsigned sub_poly_to_connect = 0;
33375
33376 // Store the vertex number on the destination boundary
33377 unsigned n_vertex_connection_initial = 0;
33378
33379 // Flags only used in a distributed mesh
33380 // ----------------------------------------
33381 // Flag to indicate we are trying to connect to an split boundary
33382 bool connecting_to_an_split_boundary = false;
33383
33384 // Flag to indicate we are trying to connecto to an internal
33385 // boundary that is overlaped by a shared boundary)
33386 bool connecting_to_an_overlaped_boundary = false;
33387
33388#ifdef OOMPH_HAS_MPI
33389 if (this->is_mesh_distributed())
33390 {
33391 // We can only connect to an original boundary, check if the
33392 // boundary was splitted during the distribution process to
33393 // consider all the chunks (sub-polylines) of the boundary
33394 if (this->boundary_was_splitted(dst_bnd_id_initial))
33395 {
33396 connecting_to_an_split_boundary = true;
33397 } // if (this->boundary_was_splitted(dst_bnd_id_initial))
33398
33399 // Check if the destination boundary, or any of its chunks is
33400 // marked to be overlapped by a shared boundary, if that is the
33401 // case we can only connect to the chunks that are not
33402 // overlapped by shared boundaries (the shared boundaries are in
33403 // charge of generating the connections with original boundaries
33404 // and with themselves)
33405 if (connecting_to_an_split_boundary)
33406 {
33407 // Get the number of chucks that represent the destination
33408 // boundary
33409 const unsigned n_sub_poly =
33410 this->nboundary_subpolylines(dst_bnd_id_initial);
33411 // Now loop over the chunks of the destination boundary and if
33412 // any of them is marked to be overlaped by a shared boundary
33413 // then set the flag and break the loop
33414 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33415 {
33416 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33417 ii))
33418 {
33419 // Mark the boundary as being overlaped by a shared
33420 // boundary
33421 connecting_to_an_overlaped_boundary = true;
33422 // Break, no need to look for more overlapings
33423 break;
33424 } // if (boundary_marked_as_shared_boundary(...))
33425 } // for (ii < n_sub_poly)
33426 } // if (connecting_to_an_split_boundary)
33427 else
33428 {
33429 // If not connecting to an split boundary then check if the
33430 // whole destination boundary is overlaped by an internal
33431 // boundary
33432 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_initial, 0))
33433 {
33434 // Mark the boundary as being overlaped by a shared boundary
33435 connecting_to_an_overlaped_boundary = true;
33436 } // if (boundary_marked_as_shared_boundary(...))
33437 } // else if (connecting_to_an_split_boundary)
33438
33439 } // if (this->is_mesh_distributed())
33440
33441#endif // #ifdef OOMPH_HAS_MPI
33442
33443 // If we are connecting neither to an split boundary nor an
33444 // overlaped boundary then get the pointer to the original
33445 // boundary
33446 if (!(connecting_to_an_split_boundary ||
33447 connecting_to_an_overlaped_boundary))
33448 {
33449 // Get the polyline pointer representing the destination
33450 // boundary
33451 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_initial);
33452 } // else if (NOT split, NOT overlaped)
33453
33454 // Now look for the vertex number on the destination boundary(ies)
33455 // -- in case that the boundary was split ---
33456
33457 // Do not check for same orientation, that was previously worked
33458 // by interchanging the connections boundaries (if necessary)
33459
33460 // If the boundary was not split then ...
33461 if (!connecting_to_an_split_boundary)
33462 {
33463 // ... check if the boundary is marked to be overlaped by
33464 // a shared boundary
33465 if (!connecting_to_an_overlaped_boundary)
33466 {
33467 // If that is not the case then we can safely look for the
33468 // vertex number on the destination boundary
33469 found_vertex_on_dst_boundary_initial =
33470 this->get_connected_vertex_number_on_destination_polyline(
33471 poly_to_connect_pt,
33472 src_vertex_coordinates_initial,
33473 n_vertex_connection_initial);
33474
33475 } // if (!connecting_to_an_overlaped_boundary)
33476 else
33477 {
33478 // If the whole boundary is marked to be overlaped by a shared
33479 // boundary then do nothing, the shared boundaries are already
33480 // in charge of performing the connection (it will be required
33481 // to disabled the connection) with the original boundary
33482
33483 } // else if (!connecting_to_an_overlaped_boundary)
33484
33485 } // if (!connecting_to_an_split_boundary)
33486#ifdef OOMPH_HAS_MPI
33487 else
33488 {
33489 // If the boundary was split then we need to look for the vertex
33490 // in the sub-polylines
33491
33492 // Get the sub-polylines vector
33493 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
33494 this->boundary_subpolylines(dst_bnd_id_initial);
33495
33496 // Get the number of sub-polylines
33497 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33498#ifdef PARANOID
33499 if (nsub_poly <= 1)
33500 {
33501 std::ostringstream error_message;
33502 error_message << "The boundary (" << dst_bnd_id_initial << ") was "
33503 << "marked to be splitted but\n"
33504 << "there are only (" << nsub_poly << ") polylines to "
33505 << "represent it.\n";
33506 throw OomphLibError(
33507 error_message.str(),
33508 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33509 OOMPH_EXCEPTION_LOCATION);
33510 } // if (nsub_poly <= 1)
33511#endif
33512 // We need to check if the boundary is marked to be overlaped by
33513 // a shared boundary, if that is the case we need to check for
33514 // each indivual subpolyline, and for those overlaped by a
33515 // shared polyline do nothing, the shared polylines have already
33516 // deal with these connections
33517
33518 // ... check if the boundary is marked to be overlaped by
33519 // a shared boundary
33520 if (!connecting_to_an_overlaped_boundary)
33521 {
33522 // The boundary is not overlapped by shared boundaries, we can
33523 // work without checking the subpolylines individually (non of
33524 // them are overlapped by a shared boundary)
33525
33526 // Look for the vertex number to connect on each of the
33527 // subpolyines
33528 for (unsigned isub = 0; isub < nsub_poly; isub++)
33529 {
33530 // Assign the pointer to the sub-polyline
33531 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33532 // Search for the vertex in the current sub-polyline
33533 found_vertex_on_dst_boundary_initial =
33534 this->get_connected_vertex_number_on_destination_polyline(
33535 poly_to_connect_pt,
33536 src_vertex_coordinates_initial,
33537 n_vertex_connection_initial);
33538
33539 // If we have found the vertex to connect then break the
33540 // loop
33541 if (found_vertex_on_dst_boundary_initial)
33542 {
33543 // But first save the subpoly number (chunk), that will be
33544 // used to perform the connection
33545 sub_poly_to_connect = isub;
33546 break;
33547 } // if (found_vertex_on_dst_boundary_initial)
33548
33549 } // for (isub < nsub_poly)
33550
33551 } // if (!connecting_to_an_overlaped_boundary)
33552 else
33553 {
33554 // If connecting to an overlapped boundary then we ignore the
33555 // subpolylines overlapped by shared boundaries and only look
33556 // on the sub-polylines that are not marked as being overlaped
33557 // by shared boundaries
33558
33559 // Look for the vertex number to connect on each of the
33560 // subpolyines
33561 for (unsigned isub = 0; isub < nsub_poly; isub++)
33562 {
33563 // Only work with those sub-polylines that are not overlaped
33564 // by shared boundaries
33565 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_initial,
33566 isub))
33567 {
33568 // Assign the pointer to the sub-polyline
33569 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33570
33571 // Search for the vertex in the current sub-polyline
33572 found_vertex_on_dst_boundary_initial =
33573 this->get_connected_vertex_number_on_destination_polyline(
33574 poly_to_connect_pt,
33575 src_vertex_coordinates_initial,
33576 n_vertex_connection_initial);
33577
33578 // Was the vertex found?
33579 if (found_vertex_on_dst_boundary_initial)
33580 {
33581 // But first save the subpoly number (chunk), that will
33582 // be used to perform the connection
33583 sub_poly_to_connect = isub;
33584 break;
33585 } // if (found_vertex_on_dst_boundary_initial)
33586
33587 } // if (not overlaped by shared boundary)
33588
33589 } // for (isub < nsub_poly)
33590
33591 } // else if (!connecting_to_an_overlaped_boundary)
33592
33593 } // else if (!connecting_to_an_split_boundary)
33594#endif // #ifdef OOMPH_HAS_MPI
33595
33596 // If not found it may be that the connection information is
33597 // inverted
33598 if (!found_vertex_on_dst_boundary_initial)
33599 {
33600 // Is the mesh distributed?
33601#ifdef OOMPH_HAS_MPI
33602 if (this->is_mesh_distributed())
33603 {
33604 // If the mesh is distributed and the vertex number was not
33605 // found, that means that the boundary (or vertex) to connect
33606 // in the destination boundary is not in the current
33607 // processor. In that case suspend the connection
33608 polyline_pt->suspend_initial_vertex_connected();
33609 // Add the polyline to the vector of polylines whose
33610 // connection will be resumed at the end of the adaptation
33611 // process
33612 resume_initial_connection_polyline_pt.push_back(polyline_pt);
33613 // The shared boundaries are marked to connect to the initial
33614 // vertex of the polyline (remember that a shared boundary
33615 // stops adding nodes when it finds a node on an original
33616 // boundary) -- The initial vertex is now a base node
33617 }
33618 else
33619#endif // #ifdef OOMPH_HAS_MPI
33620 {
33621#ifdef PARANOID
33622 // If not found then there is a problem with the vertices
33623 // Get the associated boundary id of the current polyline
33624 const unsigned bnd_id = polyline_pt->boundary_id();
33625 std::ostringstream error_message;
33626 error_message
33627 << "INITIAL VERTEX CONNECTION\n"
33628 << "It was not possible to find the associated "
33629 << "vertex number on the destination boundary\n"
33630 << "The current boundary (" << bnd_id << ") is marked to have"
33631 << "a connection at the\nINITIAL vertex ("
33632 << src_vertex_coordinates_initial[0] << ","
33633 << src_vertex_coordinates_initial[1] << ")\n"
33634 << "with boundary (" << dst_bnd_id_initial << ")\n"
33635 << "This is the list of vertices on the destination boundary\n";
33636 // Get the pointer to the associated polyline by using the
33637 // boundary id
33638 TriangleMeshPolyLine* dst_polyline =
33639 this->boundary_polyline_pt(dst_bnd_id_initial);
33640 // The number of vertices on the destination boundary
33641 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33642 // Loop over the vertices print them
33643 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33644 {
33645 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33646 error_message << "Vertex#(i): (" << current_vertex[0] << ", "
33647 << current_vertex[1] << ")\n";
33648 }
33649 throw OomphLibError(
33650 error_message.str(),
33651 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33652 OOMPH_EXCEPTION_LOCATION);
33653#endif
33654
33655 } // else if (this->is_mesh_distributed())
33656
33657 } // if (!found_vertex_on_dst_boundary_initial)
33658 else
33659 {
33660 // Set the vertex number on the destination boundary
33661 polyline_pt->initial_vertex_connected_n_vertex() =
33662 n_vertex_connection_initial;
33663
33664 // Set the chunk number on the destination boundary
33665 polyline_pt->initial_vertex_connected_n_chunk() = sub_poly_to_connect;
33666
33667 } // else if (!found_vertex_on_dst_boundary_initial)
33668
33669 } // if (polyline_pt->is_initial_vertex_connected())
33670
33671 // ------------------------------------------------------------------
33672 // Is the final vertex connected?
33673 if (polyline_pt->is_final_vertex_connected())
33674 {
33675 // The pointer to the boundary to connect
33676 TriangleMeshPolyLine* poly_to_connect_pt = 0;
33677
33678 // Get the boundary id of the destination/connected boundary
33679 const unsigned dst_bnd_id_final =
33680 polyline_pt->final_vertex_connected_bnd_id();
33681
33682 // Get the final vertex on the current boundary
33683 const unsigned tmp_n_vertices = polyline_pt->nvertex();
33684 Vector<double> src_vertex_coordinates_final =
33685 polyline_pt->vertex_coordinate(tmp_n_vertices - 1);
33686
33687
33688#ifdef PARANOID
33689 // Is the mesh distributed?
33690#ifdef OOMPH_HAS_MPI
33691 if (this->is_mesh_distributed())
33692 {
33693 // Get the initial shared boundary id
33694 const unsigned init_shd_bnd_id = this->initial_shared_boundary_id();
33695 // Is the boundary to connect a shared boundary
33696 if (dst_bnd_id_final >= init_shd_bnd_id)
33697 {
33698 // Get the current polyline original boundary id
33699 const unsigned bnd_id = polyline_pt->boundary_id();
33700 std::ostringstream error_message;
33701 error_message
33702 << "FINAL VERTEX CONNECTION\n"
33703 << "The current original boundary is trying to connect to a\n"
33704 << "shared boundary, this is not allowed. In this case the\n"
33705 << "shared boundary should be the one that connects with the\n"
33706 << "original boundary\n"
33707 << "The current boundary (" << bnd_id << ") is marked to have "
33708 << "a connection at the\nFINAL vertex ("
33709 << src_vertex_coordinates_final[0] << ","
33710 << src_vertex_coordinates_final[1] << ")\n"
33711 << "with boundary (" << dst_bnd_id_final << ")\n"
33712 << "This is the list of vertices on the destination boundary\n";
33713 // Get the pointer to the associated polyline by using the
33714 // boundary id
33715 TriangleMeshPolyLine* dst_polyline =
33716 this->boundary_polyline_pt(dst_bnd_id_final);
33717 // The number of vertices on the destination boundary
33718 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
33719 // Loop over the vertices print them
33720 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
33721 {
33722 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
33723 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
33724 << ", " << current_vertex[1] << ")\n";
33725 }
33726 throw OomphLibError(
33727 error_message.str(),
33728 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33729 OOMPH_EXCEPTION_LOCATION);
33730 } // if (dst_bnd_id_initial >= init_shd_bnd_id)
33731
33732 } // if (this->is_mesh_distributed())
33733#endif // #ifdef OOMPH_HAS_MPI
33734
33735#endif // #ifdef PARANOID
33736
33737 // Flag to indicate if the vertex was found on the destination
33738 // boundary
33739 bool found_vertex_on_dst_boundary_final = false;
33740
33741 // Flag that stores the chunk number to connect (only used in
33742 // distributed meshes)
33743 unsigned sub_poly_to_connect = 0;
33744
33745 // Store the vertex number on the destination boundary
33746 unsigned n_vertex_connection_final = 0;
33747
33748 // Flags only used in a distributed mesh
33749 // ----------------------------------------
33750 // Flag to indicate we are trying to connect to an split boundary
33751 bool connecting_to_an_split_boundary = false;
33752
33753 // Flag to indicate we are trying to connecto to an internal
33754 // boundary that is overlaped by a shared boundary)
33755 bool connecting_to_an_overlaped_boundary = false;
33756
33757#ifdef OOMPH_HAS_MPI
33758 if (this->is_mesh_distributed())
33759 {
33760 // We can only connect to an original boundary, check if the
33761 // boundary was splitted during the distribution process to
33762 // consider all the chunks (sub-polylines) of the boundary
33763 if (this->boundary_was_splitted(dst_bnd_id_final))
33764 {
33765 connecting_to_an_split_boundary = true;
33766 } // if (this->boundary_was_splitted(dst_bnd_id_final))
33767
33768 // Check if the destination boundary, or any of its chunks is
33769 // marked to be overlapped by a shared boundary, if that is the
33770 // case we can only connect to the chunks that are not
33771 // overlapped by shared boundaries (the shared boundaries are in
33772 // charge of generating the connections with original boundaries
33773 // and with themselves)
33774 if (connecting_to_an_split_boundary)
33775 {
33776 // Get the number of chucks that represent the destination
33777 // boundary
33778 const unsigned n_sub_poly =
33779 this->nboundary_subpolylines(dst_bnd_id_final);
33780 // Now loop over the chunks of the destination boundary and if
33781 // any of them is marked to be overlaped by a shared boundary
33782 // then set the flag and break the loop
33783 for (unsigned ii = 0; ii < n_sub_poly; ii++)
33784 {
33785 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, ii))
33786 {
33787 // Mark the boundary as being overlaped by a shared
33788 // boundary
33789 connecting_to_an_overlaped_boundary = true;
33790 // Break, no need to look for more overlapings
33791 break;
33792 } // if (boundary_marked_as_shared_boundary(...))
33793 } // for (ii < n_sub_poly)
33794 } // if (connecting_to_an_split_boundary)
33795 else
33796 {
33797 // If not connecting to an split boundary then check if the
33798 // whole destination boundary is overlaped by an internal
33799 // boundary
33800 if (this->boundary_marked_as_shared_boundary(dst_bnd_id_final, 0))
33801 {
33802 // Mark the boundary as being overlaped by a shared boundary
33803 connecting_to_an_overlaped_boundary = true;
33804 } // if (boundary_marked_as_shared_boundary(...))
33805 } // else if (connecting_to_an_split_boundary)
33806
33807 } // if (this->is_mesh_distributed())
33808
33809#endif // #ifdef OOMPH_HAS_MPI
33810
33811 // If we are connecting neither to an split boundary nor an
33812 // overlaped boundary then get the pointer to the original
33813 // boundary
33814 if (!(connecting_to_an_split_boundary ||
33815 connecting_to_an_overlaped_boundary))
33816 {
33817 // Get the polyline pointer representing the destination
33818 // boundary
33819 poly_to_connect_pt = this->boundary_polyline_pt(dst_bnd_id_final);
33820 } // else if (NOT split, NOT overlaped)
33821
33822 // Now look for the vertex number on the destination boundary(ies)
33823 // -- in case that the boundary was split ---
33824
33825 // Do not check for same orientation, that was previously worked
33826 // by interchanging the connections boundaries (if necessary)
33827
33828 // If the boundary was not split then ...
33829 if (!connecting_to_an_split_boundary)
33830 {
33831 // ... check if the boundary is marked to be overlaped by
33832 // a shared boundary
33833 if (!connecting_to_an_overlaped_boundary)
33834 {
33835 // If that is not the case then we can safely look for the
33836 // vertex number on the destination boundary
33837 found_vertex_on_dst_boundary_final =
33838 this->get_connected_vertex_number_on_destination_polyline(
33839 poly_to_connect_pt,
33840 src_vertex_coordinates_final,
33841 n_vertex_connection_final);
33842
33843 } // if (!connecting_to_an_overlaped_boundary)
33844 else
33845 {
33846 // If the whole boundary is marked to be overlaped by a shared
33847 // boundary then do nothing, the shared boundaries are already
33848 // in charge of performing the connection (it will be required
33849 // to disabled the connection) with the original boundary
33850
33851 } // else if (!connecting_to_an_overlaped_boundary)
33852
33853 } // if (!connecting_to_an_split_boundary)
33854#ifdef OOMPH_HAS_MPI
33855 else
33856 {
33857 // If the boundary was split then we need to look for the vertex
33858 // in the sub-polylines
33859
33860 // Get the sub-polylines vector
33861 Vector<TriangleMeshPolyLine*> tmp_vector_subpolylines =
33862 this->boundary_subpolylines(dst_bnd_id_final);
33863
33864 // Get the number of sub-polylines
33865 const unsigned nsub_poly = tmp_vector_subpolylines.size();
33866#ifdef PARANOID
33867 if (nsub_poly <= 1)
33868 {
33869 std::ostringstream error_message;
33870 error_message << "The boundary (" << dst_bnd_id_final << ") was "
33871 << "marked to be splitted but\n"
33872 << "there are only (" << nsub_poly << ") polylines to "
33873 << "represent it.\n";
33874 throw OomphLibError(
33875 error_message.str(),
33876 "RefineableTriangleMesh::restore_polyline_connections_helper()",
33877 OOMPH_EXCEPTION_LOCATION);
33878 } // if (nsub_poly <= 1)
33879#endif
33880 // We need to check if the boundary is marked to be overlaped by
33881 // a shared boundary, if that is the case we need to check for
33882 // each indivual subpolyline, and for those overlaped by a
33883 // shared polyline do nothing, the shared polylines have already
33884 // deal with these connections
33885
33886 // ... check if the boundary is marked to be overlaped by
33887 // a shared boundary
33888 if (!connecting_to_an_overlaped_boundary)
33889 {
33890 // The boundary is not overlapped by shared boundaries, we can
33891 // work without checking the subpolylines individually (non of
33892 // them are overlapped by a shared boundary)
33893
33894 // Look for the vertex number to connect on each of the
33895 // subpolyines
33896 for (unsigned isub = 0; isub < nsub_poly; isub++)
33897 {
33898 // Assign the pointer to the sub-polyline
33899 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33900 // Search for the vertex in the current sub-polyline
33901 found_vertex_on_dst_boundary_final =
33902 this->get_connected_vertex_number_on_destination_polyline(
33903 poly_to_connect_pt,
33904 src_vertex_coordinates_final,
33905 n_vertex_connection_final);
33906
33907 // If we have found the vertex to connect then break the
33908 // loop
33909 if (found_vertex_on_dst_boundary_final)
33910 {
33911 // But first save the subpoly number (chunk), that will be
33912 // used to perform the connection
33913 sub_poly_to_connect = isub;
33914 break;
33915 } // if (found_vertex_on_dst_boundary_initial)
33916
33917 } // for (isub < nsub_poly)
33918
33919 } // if (!connecting_to_an_overlaped_boundary)
33920 else
33921 {
33922 // If connecting to an overlapped boundary then we ignore the
33923 // subpolylines overlapped by shared boundaries and only look
33924 // on the sub-polylines that are not marked as being overlaped
33925 // by shared boundaries
33926
33927 // Look for the vertex number to connect on each of the
33928 // subpolyines
33929 for (unsigned isub = 0; isub < nsub_poly; isub++)
33930 {
33931 // Only work with those sub-polylines that are not overlaped
33932 // by shared boundaries
33933 if (!this->boundary_marked_as_shared_boundary(dst_bnd_id_final,
33934 isub))
33935 {
33936 // Assign the pointer to the sub-polyline
33937 poly_to_connect_pt = tmp_vector_subpolylines[isub];
33938
33939 // Search for the vertex in the current sub-polyline
33940 found_vertex_on_dst_boundary_final =
33941 this->get_connected_vertex_number_on_destination_polyline(
33942 poly_to_connect_pt,
33943 src_vertex_coordinates_final,
33944 n_vertex_connection_final);
33945
33946 // Was the vertex found?
33947 if (found_vertex_on_dst_boundary_final)
33948 {
33949 // But first save the subpoly number (chunk), that will
33950 // be used to perform the connection
33951 sub_poly_to_connect = isub;
33952 break;
33953 } // if (found_vertex_on_dst_boundary_final)
33954
33955 } // if (not overlaped by shared boundary)
33956
33957 } // for (isub < nsub_poly)
33958
33959 } // else if (!connecting_to_an_overlaped_boundary)
33960
33961 } // else if (!connecting_to_an_split_boundary)
33962#endif // #ifdef OOMPH_HAS_MPI
33963
33964 // If not found it may be that the connection information is
33965 // inverted
33966 if (!found_vertex_on_dst_boundary_final)
33967 {
33968 // Is the mesh distributed?
33969#ifdef OOMPH_HAS_MPI
33970 if (this->is_mesh_distributed())
33971 {
33972 // If the mesh is distributed and the vertex number was not
33973 // found, that means that the boundary (or vertex) to connect
33974 // in the destination boundary is not in the current
33975 // processor. In that suspend the connection
33976 polyline_pt->suspend_final_vertex_connected();
33977 // Add the polyline to the vector of polylines whose
33978 // connection will be resumed at the end of the adaptation
33979 // process
33980 resume_final_connection_polyline_pt.push_back(polyline_pt);
33981 // The shared boundaries are marked to connect to the final
33982 // vertex of the polyline (remember that a shared boundary
33983 // stops adding nodes when it finds a node on an original
33984 // boundary) -- The final vertex is now a base node
33985 } // if (this->is_mesh_distributed())
33986 else
33987#endif // #ifdef OOMPH_HAS_MPI
33988 {
33989#ifdef PARANOID
33990 // If not found then there is a problem with the vertices
33991 // Get the associated boundary id of the current polyline
33992 const unsigned bnd_id = polyline_pt->boundary_id();
33993 std::ostringstream error_message;
33994 error_message
33995 << "FINAL VERTEX CONNECTION\n"
33996 << "It was not possible to find the associated "
33997 << "vertex number on the destination boundary\n"
33998 << "The current boundary (" << bnd_id << ") is marked to have "
33999 << "a connection at the\nFINAL vertex ("
34000 << src_vertex_coordinates_final[0] << ","
34001 << src_vertex_coordinates_final[1] << ")\n"
34002 << "with boundary (" << dst_bnd_id_final << ")\n"
34003 << "This is the list of vertices on the destination boundary\n";
34004 // Get the pointer to the associated polyline by using the
34005 // boundary id
34006 TriangleMeshPolyLine* dst_polyline =
34007 this->boundary_polyline_pt(dst_bnd_id_final);
34008 // The number of vertices on the destination boundary
34009 const unsigned n_vertex_dst_boundary = dst_polyline->nvertex();
34010 // Loop over the vertices print them
34011 for (unsigned i = 0; i < n_vertex_dst_boundary; i++)
34012 {
34013 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34014 error_message << "Vertex#(" << i << "): (" << current_vertex[0]
34015 << ", " << current_vertex[1] << ")\n";
34016 }
34017 throw OomphLibError(
34018 error_message.str(),
34019 "RefineableTriangleMesh::restore_polyline_connections_helper()",
34020 OOMPH_EXCEPTION_LOCATION);
34021#endif
34022 } // else if (this->is_mesh_distributed())
34023
34024 } // if (!found_vertex_on_dst_boundary_final)
34025 else
34026 {
34027 // Set the vertex number on the destination boundary
34028 polyline_pt->final_vertex_connected_n_vertex() =
34029 n_vertex_connection_final;
34030
34031 // Set the chunk number on the destination boundary
34032 polyline_pt->final_vertex_connected_n_chunk() = sub_poly_to_connect;
34033
34034 } // else if (!found_vertex_on_dst_boundary_final)
34035
34036 } // if (polyline_pt->is_final_vertex_connected())
34037 }
34038
34039 //=========================================================================
34040 /// Resume the boundary connections that may have been
34041 /// suspended because the destination boundary is no part of the
34042 /// domain. The connections are no permanently suspended because if
34043 /// load balance takes place the destination boundary may be part of
34044 /// the new domain representation therefore the connection would
34045 /// exist
34046 //=========================================================================
34047 template<class ELEMENT>
34049 Vector<TriangleMeshPolyLine*>& resume_initial_connection_polyline_pt,
34050 Vector<TriangleMeshPolyLine*>& resume_final_connection_polyline_pt)
34051 {
34052 // Get the number of polylines that require to resume the connection
34053 // at the initial vertex
34054 const unsigned n_initial_poly =
34055 resume_initial_connection_polyline_pt.size();
34056 // Loop over the polylines that require to resume the connection
34057 // at the initial vertex
34058 for (unsigned p = 0; p < n_initial_poly; p++)
34059 {
34060 // Get the polyline
34061 TriangleMeshPolyLine* tmp_poly_pt =
34062 resume_initial_connection_polyline_pt[p];
34063 // Resume the connection with the initial vertex
34064 tmp_poly_pt->resume_initial_vertex_connected();
34065 } // for (p < n_initial_poly)
34066
34067 // Get the number of polylines that require to resume the connection
34068 // at the final vertex
34069 const unsigned n_final_poly = resume_final_connection_polyline_pt.size();
34070 // Loop over the polylines that require to resume the connection at
34071 // the final vertex
34072 for (unsigned p = 0; p < n_final_poly; p++)
34073 {
34074 // Get the polyline
34075 TriangleMeshPolyLine* tmp_poly_pt =
34076 resume_final_connection_polyline_pt[p];
34077 // Resume the connection with the final vertex
34078 tmp_poly_pt->resume_final_vertex_connected();
34079 } // for (p < n_final_poly)
34080
34081 // Clear the storage
34082 resume_initial_connection_polyline_pt.clear();
34083 resume_final_connection_polyline_pt.clear();
34084 }
34085
34086 //=========================================================================
34087 /// Gets the associated vertex number according to the vertex
34088 /// coordinates on the destination boundary
34089 //=========================================================================
34090 template<class ELEMENT>
34093 Vector<double>& vertex_coordinates,
34094 const unsigned& dst_bnd_id,
34095 unsigned& vertex_number)
34096 {
34097 bool found_associated_vertex_number = false;
34098
34099 // Get the pointer to the associated polyline by using the boundary id
34100 TriangleMeshPolyLine* dst_polyline = this->boundary_polyline_pt(dst_bnd_id);
34101
34102 const unsigned n_vertices = dst_polyline->nvertex();
34103
34104 // Loop over the vertices and return the closest vertex
34105 // to the given vertex coordinates
34106 for (unsigned i = 0; i < n_vertices; i++)
34107 {
34108 Vector<double> current_vertex = dst_polyline->vertex_coordinate(i);
34109
34110 double error = (vertex_coordinates[0] - current_vertex[0]) *
34111 (vertex_coordinates[0] - current_vertex[0]) +
34112 (vertex_coordinates[1] - current_vertex[1]) *
34113 (vertex_coordinates[1] - current_vertex[1]);
34114
34115 error = sqrt(error);
34116
34118 {
34119 vertex_number = i;
34120 found_associated_vertex_number = true;
34121 break;
34122 }
34123 }
34124
34125 return found_associated_vertex_number;
34126 }
34127
34128 //=========================================================================
34129 /// Helper function that updates the input polygon's PSLG
34130 /// by using the end-points of elements from FaceMesh(es) that are
34131 /// constructed for the boundaries associated with the segments of the
34132 /// polygon. Optional boolean is used to run it as test only (if
34133 /// true is specified as input) in which case polygon isn't actually
34134 /// modified. Returned boolean indicates if polygon was (or would have
34135 /// been -- if called with check_only=false) changed.
34136 //=========================================================================
34137 template<class ELEMENT>
34139 TriangleMeshPolygon* polygon_pt, const bool& check_only)
34140 {
34141#ifdef PARANOID
34142 // If the mesh is marked as distributed this method can not be
34143 // called since there is no guarantee of creating (distributed)
34144 // meshes that match in the number and position of nodes at their
34145 // shared boundaries. The only exececption is when called with
34146 // check_only=true, since no boundary updating is performed
34147 if (this->is_mesh_distributed() && !check_only)
34148 {
34149 std::stringstream error_message;
34150 error_message
34151 << "The updating of polygons of a distributed mesh can ONLY be\n"
34152 << "performed using the element's area associated to the halo(ed)\n"
34153 << "elements.\n"
34154 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34155 << "option if you are working with a distributed mesh, OR\n"
34156 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34157 << "if the mesh is marked as distributed\n\n";
34158 throw OomphLibError(
34159 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34160 } // if (this->is_mesh_distributed())
34161#endif
34162
34163 // Boolean that indicates whether an actual update of the polygon
34164 // was performed or not
34165 bool unrefinement_was_performed = false;
34166 bool refinement_was_performed = false;
34167 bool max_length_applied = false;
34168
34169 // Loop over the number of polylines
34170 const unsigned n_polyline = polygon_pt->npolyline();
34171
34172 // Get face mesh representation of all polylines, possibly
34173 // with segments re-distributed to maintain an approximately
34174 // even sub-division of the polygon
34175 Vector<Mesh*> face_mesh_pt;
34176 get_face_mesh_representation(polygon_pt, face_mesh_pt);
34177
34178 // Create vertices for the polylines by using the vertices
34179 // of the FaceElements
34180 Vector<double> vertex_coord(3); // zeta,x,y
34181 Vector<double> bound_left(1);
34182 Vector<double> bound_right(1);
34183
34184 for (unsigned p = 0; p < n_polyline; p++)
34185 {
34186 // Set of coordinates that will be placed on the boundary
34187 // Set entries are ordered on first entry in vector which stores
34188 // the boundary coordinate so the vertices come out in order!
34189 std::set<Vector<double>> vertex_nodes;
34190
34191 // Get the boundary id
34192 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
34193
34194 // Get the chunk number
34195 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
34196
34197 // Loop over the face elements (ordered) and add their vertices
34198 unsigned n_face_element = face_mesh_pt[p]->nelement();
34199 for (unsigned e = 0; e < n_face_element; ++e)
34200 {
34201 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34202
34203#ifdef OOMPH_HAS_MPI
34204 // Only work with non-halo elements if the mesh is distributed
34205 if (this->is_mesh_distributed() && el_pt->is_halo())
34206 {
34207 continue;
34208 }
34209#endif
34210
34211 unsigned n_node = el_pt->nnode();
34212
34213 // Add the left-hand node to the set:
34214
34215 // Boundary coordinate
34216 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
34217 vertex_coord[0] = bound_left[0];
34218
34219 // Actual coordinates
34220 for (unsigned i = 0; i < 2; i++)
34221 {
34222 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34223 }
34224 vertex_nodes.insert(vertex_coord);
34225
34226 // Add the right-hand nodes to the set:
34227
34228 // Boundary coordinate
34229 el_pt->node_pt(n_node - 1)
34230 ->get_coordinates_on_boundary(bound, bound_right);
34231 vertex_coord[0] = bound_right[0];
34232
34233 // Actual coordinates
34234 for (unsigned i = 0; i < 2; i++)
34235 {
34236 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34237 }
34238 vertex_nodes.insert(vertex_coord);
34239 }
34240
34241 // Now turn into vector for ease of handling...
34242 unsigned n_poly_vertex = vertex_nodes.size();
34243 Vector<Vector<double>> tmp_vector_vertex_node(n_poly_vertex);
34244 unsigned count = 0;
34245 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34246 it != vertex_nodes.end();
34247 ++it)
34248 {
34249 tmp_vector_vertex_node[count].resize(3);
34250 tmp_vector_vertex_node[count][0] = (*it)[0];
34251 tmp_vector_vertex_node[count][1] = (*it)[1];
34252 tmp_vector_vertex_node[count][2] = (*it)[2];
34253 ++count;
34254 }
34255
34256 // Size of the vector
34257 unsigned n_vertex = tmp_vector_vertex_node.size();
34258
34259 // Tolerance below which the middle point can be deleted
34260 // (ratio of deflection to element length)
34261 double unrefinement_tolerance =
34262 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
34263
34264 //------------------------------------------------------
34265 // Unrefinement
34266 //------------------------------------------------------
34267 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34268 {
34269 unrefinement_was_performed = unrefine_boundary(bound,
34270 chunk,
34271 tmp_vector_vertex_node,
34272 unrefinement_tolerance,
34273 check_only);
34274
34275 // In this case the "unrefinement_was_performed" variable
34276 // tell us if the update had been performed when calling
34277 // with check_oly=false
34278 if (check_only && unrefinement_was_performed)
34279 {
34280 // Cleanup (but only the elements -- the nodes still exist in
34281 // the bulk mesh!
34282 for (unsigned p = 0; p < n_polyline; p++)
34283 {
34284 face_mesh_pt[p]->flush_node_storage();
34285 delete face_mesh_pt[p];
34286 }
34287 return true;
34288 }
34289
34290 } // end of unrefinement
34291
34292 // Do not perform refinement if there are no more than two vertices
34293 // New size of the vector
34294 n_vertex = tmp_vector_vertex_node.size();
34295
34296 //------------------------------------------------
34297 // Refinement
34298 //------------------------------------------------
34299 double refinement_tolerance =
34300 polygon_pt->polyline_pt(p)->refinement_tolerance();
34301 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34302 {
34303 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34304 tmp_vector_vertex_node,
34305 refinement_tolerance,
34306 check_only);
34307
34308 // In this case the "refinement_was_performed" variable
34309 // tell us if the update had been performed when calling
34310 // with check_only=false
34311 if (check_only && refinement_was_performed)
34312 {
34313 // Cleanup (but only the elements -- the nodes still exist in
34314 // the bulk mesh!
34315 for (unsigned p = 0; p < n_polyline; p++)
34316 {
34317 face_mesh_pt[p]->flush_node_storage();
34318 delete face_mesh_pt[p];
34319 }
34320 return true;
34321 }
34322
34323 } // end refinement
34324
34325 // Do not perform maximum length constraint if there are no more than
34326 // two vertices
34327 // New size of the vector
34328 n_vertex = tmp_vector_vertex_node.size();
34329
34330 //------------------------------------------------
34331 // Maximum length constrait
34332 //-----------------------------------------------
34333 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
34334 if (maximum_length > 0.0 && n_vertex >= 2)
34335 {
34336 max_length_applied = apply_max_length_constraint(
34337 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34338
34339 // In this case the max length criteria was applied, check if
34340 // check_only=false
34341 if (check_only && max_length_applied)
34342 {
34343 // Cleanup (but only the elements -- the nodes still exist in
34344 // the bulk mesh!
34345 for (unsigned p = 0; p < n_polyline; p++)
34346 {
34347 face_mesh_pt[p]->flush_node_storage();
34348 delete face_mesh_pt[p];
34349 }
34350 return true;
34351 }
34352 }
34353
34354 // For further processing the three-dimensional vector
34355 // has to be reduced to a two-dimensional vector
34356 n_vertex = tmp_vector_vertex_node.size();
34357 Vector<Vector<double>> vector_vertex_node(n_vertex);
34358
34359 for (unsigned i = 0; i < n_vertex; i++)
34360 {
34361 vector_vertex_node[i].resize(2);
34362 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
34363 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
34364 }
34365
34366#ifdef OOMPH_HAS_MPI
34367 // Only perform this checking if the mesh is not distributed. When
34368 // the mesh is distributed the polylines continuity is addressed in
34369 // the sort_polylines_helper() method
34370 if (!this->is_mesh_distributed())
34371#endif
34372 {
34373 if ((p > 0) && !check_only)
34374 {
34375 // Final end point of previous line
34376 Vector<double> final_vertex_of_previous_segment;
34377 unsigned n_prev_vertex =
34378 polygon_pt->curve_section_pt(p - 1)->nvertex();
34379 final_vertex_of_previous_segment =
34380 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
34381 1);
34382
34383 unsigned prev_seg_boundary_id =
34384 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34385
34386 // Find the error between the final vertex of the previous
34387 // line and the first vertex of the current line
34388 double error = 0.0;
34389 for (unsigned i = 0; i < 2; i++)
34390 {
34391 const double dist = final_vertex_of_previous_segment[i] -
34392 (*vector_vertex_node.begin())[i];
34393 error += dist * dist;
34394 }
34395 error = sqrt(error);
34396
34397 // If the error is bigger than the tolerance then
34398 // we probably need to reverse, but better check
34400 {
34401 // Find the error between the final vertex of the previous
34402 // line and the last vertex of the current line
34403 double rev_error = 0.0;
34404 for (unsigned i = 0; i < 2; i++)
34405 {
34406 const double dist = final_vertex_of_previous_segment[i] -
34407 (*--vector_vertex_node.end())[i];
34408 rev_error += dist * dist;
34409 }
34410 rev_error = sqrt(rev_error);
34411
34412 if (rev_error >
34414 {
34415 // It could be possible that the first segment be reversed and we
34416 // did not notice it because this check does not apply for the
34417 // first segment. We can verify if the first segment is reversed
34418 // by using the vertex number 1
34419 if (p == 1)
34420 {
34421 // Initial end point of previous line
34422 Vector<double> initial_vertex_of_previous_segment;
34423
34424 initial_vertex_of_previous_segment =
34425 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
34426
34427 unsigned prev_seg_boundary_id =
34428 polygon_pt->curve_section_pt(p - 1)->boundary_id();
34429
34430 // Find the error between the initial vertex of the previous
34431 // line and the first vertex of the current line
34432 double error = 0.0;
34433 for (unsigned i = 0; i < 2; i++)
34434 {
34435 const double dist = initial_vertex_of_previous_segment[i] -
34436 (*vector_vertex_node.begin())[i];
34437 error += dist * dist;
34438 }
34439 error = sqrt(error); // Reversed only the previous one
34440
34441 // If the error is bigger than the tolerance then
34442 // we probably need to reverse, but better check
34443 if (error >
34445 {
34446 // Find the error between the final vertex of the previous
34447 // line and the last vertex of the current line
34448 double rev_error = 0.0;
34449 for (unsigned i = 0; i < 2; i++)
34450 {
34451 const double dist = initial_vertex_of_previous_segment[i] -
34452 (*--vector_vertex_node.end())[i];
34453 rev_error += dist * dist;
34454 }
34455 rev_error =
34456 sqrt(rev_error); // Reversed both the current one and
34457 // the previous one
34458
34459 if (rev_error >
34461 {
34462 std::ostringstream error_stream;
34463 error_stream
34464 << "The distance between the first node of the current\n"
34465 << "line segment (boundary " << bound
34466 << ") and either end of "
34467 << "the previous line segment\n"
34468 << "(boundary " << prev_seg_boundary_id
34469 << ") is bigger than "
34470 << "the desired tolerance "
34472 << ".\n"
34473 << "This suggests that the polylines defining the "
34474 "polygonal\n"
34475 << "representation are not properly ordered.\n"
34476 << "Fail on last vertex of polyline: ("
34477 << prev_seg_boundary_id
34478 << ") and\nfirst vertex of polyline (" << bound
34479 << ").\nThis should have failed when first trying to "
34480 << "construct the\npolygon.\n";
34481 throw OomphLibError(error_stream.str(),
34482 OOMPH_CURRENT_FUNCTION,
34483 OOMPH_EXCEPTION_LOCATION);
34484 }
34485 else
34486 {
34487 // Reverse both
34488 // Reverse the current vector to line up with the previous
34489 // one
34490 std::reverse(vector_vertex_node.begin(),
34491 vector_vertex_node.end());
34492
34493 polygon_pt->polyline_pt(p - 1)->reverse();
34494 }
34495 }
34496 else
34497 {
34498 // Reverse the previous one
34499 polygon_pt->polyline_pt(p - 1)->reverse();
34500 }
34501
34502 } // if p == 1
34503 else
34504 {
34505 std::ostringstream error_stream;
34506 error_stream
34507 << "The distance between the first node of the current\n"
34508 << "line segment (boundary " << bound
34509 << ") and either end of "
34510 << "the previous line segment\n"
34511 << "(boundary " << prev_seg_boundary_id
34512 << ") is bigger than the "
34513 << "desired tolerance "
34515 << ".\n"
34516 << "This suggests that the polylines defining the polygonal\n"
34517 << "representation are not properly ordered.\n"
34518 << "Fail on last vertex of polyline: ("
34519 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
34520 << bound << ").\n"
34521 << "This should have failed when first trying to construct "
34522 "the\n"
34523 << "polygon.\n";
34524 throw OomphLibError(error_stream.str(),
34525 OOMPH_CURRENT_FUNCTION,
34526 OOMPH_EXCEPTION_LOCATION);
34527 }
34528 }
34529 else
34530 {
34531 // Reverse the current vector to line up with the previous one
34532 std::reverse(vector_vertex_node.begin(),
34533 vector_vertex_node.end());
34534 }
34535
34536 } // first error
34537 } // p > 0
34538 } // is mesh not distributed?
34539
34540 if (!check_only)
34541 {
34542 // Now update the polyline according to the new vertices
34543 // The new one representation
34544 TriangleMeshPolyLine* tmp_polyline_pt =
34545 new TriangleMeshPolyLine(vector_vertex_node, bound);
34546
34547 // Create a temporal "curve section" version of the recently created
34548 // polyline
34549 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
34550
34551 // Establish refinement and unrefinement tolerance
34552 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
34553 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
34554
34555 // Establish the maximum length constraint
34556 tmp_polyline_pt->set_maximum_length(maximum_length);
34557
34558 // We pass the connection information from the old polyline to
34559 // the new one
34560 this->copy_connection_information(polygon_pt->polyline_pt(p),
34561 tmp_curve_section_pt);
34562
34563 // Now update the polyline according to the new vertices but
34564 // first check if the object is allowed to delete the representation
34565 // or if it should be done by other object
34566 bool delete_it_on_destructor = false;
34567
34568 std::set<TriangleMeshCurveSection*>::iterator it =
34569 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
34570
34571 if (it != this->Free_curve_section_pt.end())
34572 {
34573 this->Free_curve_section_pt.erase(it);
34574 delete polygon_pt->curve_section_pt(p);
34575 delete_it_on_destructor = true;
34576 }
34577
34578 // ------------------------------------------------------------
34579 // Copying the new representation
34580 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
34581
34582 // Update the Boundary - Polyline map
34583 this->Boundary_curve_section_pt[bound] =
34584 polygon_pt->curve_section_pt(p);
34585
34586 if (delete_it_on_destructor)
34587 {
34588 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
34589 }
34590
34591 } // if(!check_only)
34592
34593 } // for (p < n_polyline)
34594
34595 // Cleanup (but only the elements -- the nodes still exist in
34596 // the bulk mesh!
34597 for (unsigned p = 0; p < n_polyline; p++)
34598 {
34599 face_mesh_pt[p]->flush_node_storage();
34600 delete face_mesh_pt[p];
34601 }
34602
34603 if (check_only)
34604 {
34605 // if we end up all the way down here, no update of the internal
34606 // boundaries is necessary (in case we only check)
34607 return false;
34608 }
34609 else
34610 {
34611 // if we not only check, but actually perform the update and end up
34612 // all the way down here then we indicate whether an update was performed
34613 // or not
34614 return (unrefinement_was_performed || refinement_was_performed ||
34615 max_length_applied);
34616 }
34617 }
34618
34619 //=========================================================================
34620 /// Helper function that updates the input open curve by using
34621 /// end-points of elements from FaceMesh(es) that are constructed for the
34622 /// boundaries associated with the polylines. Optional boolean is used to
34623 /// run it as test only (if true is specified as input) in which case the
34624 /// polylines are not actually modified. Returned boolean indicates if
34625 /// polylines were (or would have been -- if called with check_only=false)
34626 /// changed.
34627 //=========================================================================
34628 template<class ELEMENT>
34630 TriangleMeshOpenCurve* open_polyline_pt, const bool& check_only)
34631 {
34632#ifdef PARANOID
34633 // If the mesh is marked as distributed this method can not be
34634 // called since there is no guarantee of creating (distributed)
34635 // meshes that match in the number and position of nodes at their
34636 // shared boundaries. The only exececption is when called with
34637 // check_only=true, since no boundary updating is performed
34638 if (this->is_mesh_distributed() && !check_only)
34639 {
34640 std::stringstream error_message;
34641 error_message
34642 << "The updating of open curves of a distributed mesh can ONLY be\n"
34643 << "performed using the element's area associated to the halo(ed)\n"
34644 << "elements.\n"
34645 << "1) Make sure you have enabled the parallel mesh adaptation\n"
34646 << "option if you are working with a distributed mesh, OR\n"
34647 << "2) Make sure to call the update_..._using_elements_area() methods\n"
34648 << "if the mesh is marked as distributed\n\n";
34649 throw OomphLibError(
34650 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
34651 } // if (this->is_mesh_distributed())
34652#endif
34653
34654 // Boolean that indicates whether an actual update of the polylines
34655 // were performed or not
34656 bool unrefinement_was_performed = false;
34657 bool refinement_was_performed = false;
34658 bool max_length_applied = false;
34659
34660 // Loop over the number of polylines
34661 const unsigned n_polyline = open_polyline_pt->ncurve_section();
34662
34663 // Get face mesh representation of all polylines, possibly
34664 // with segments re-distributed to maintain an approximately
34665 // even sub-division of the polygon
34666 Vector<Mesh*> face_mesh_pt;
34667 get_face_mesh_representation(open_polyline_pt, face_mesh_pt);
34668
34669 // Create vertices for the polylines by using the vertices
34670 // of the FaceElements
34671 Vector<double> vertex_coord(3); // zeta,x,y
34672 Vector<double> bound_left(1);
34673 Vector<double> bound_right(1);
34674
34675 for (unsigned p = 0; p < n_polyline; p++)
34676 {
34677 // Set of coordinates that will be placed on the boundary
34678 // Set entries are ordered on first entry in vector which stores
34679 // the boundary coordinate so the vertices come out in order!
34680 std::set<Vector<double>> vertex_nodes;
34681
34682 // Get the boundary id
34683 const unsigned bound =
34684 open_polyline_pt->curve_section_pt(p)->boundary_id();
34685
34686 // Get the chunk number
34687 const unsigned chunk =
34688 open_polyline_pt->curve_section_pt(p)->boundary_chunk();
34689
34690 // Loop over the face elements (ordered) and add their vertices
34691 unsigned n_face_element = face_mesh_pt[p]->nelement();
34692
34693 // n_count = 0;
34694 for (unsigned e = 0; e < n_face_element; ++e)
34695 {
34696 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(e);
34697 unsigned n_node = el_pt->nnode();
34698
34699 // Add the left-hand node to the set:
34700
34701 // Boundary coordinate
34702 el_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
34703 vertex_coord[0] = bound_left[0];
34704
34705 // Actual coordinates
34706 for (unsigned i = 0; i < 2; i++)
34707 {
34708 vertex_coord[i + 1] = el_pt->node_pt(0)->x(i);
34709 }
34710 vertex_nodes.insert(vertex_coord);
34711
34712 // Add the right-hand nodes to the set:
34713
34714 // Boundary coordinate
34715 el_pt->node_pt(n_node - 1)
34716 ->get_coordinates_on_boundary(bound, bound_right);
34717 vertex_coord[0] = bound_right[0];
34718
34719 // Actual coordinates
34720 for (unsigned i = 0; i < 2; i++)
34721 {
34722 vertex_coord[i + 1] = el_pt->node_pt(n_node - 1)->x(i);
34723 }
34724 vertex_nodes.insert(vertex_coord);
34725 }
34726
34727 // Now turn into vector for ease of handling...
34728 unsigned n_poly_vertex = vertex_nodes.size();
34729 Vector<Vector<double>> tmp_vector_vertex_node(n_poly_vertex);
34730 unsigned count = 0;
34731 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
34732 it != vertex_nodes.end();
34733 ++it)
34734 {
34735 tmp_vector_vertex_node[count].resize(3);
34736 tmp_vector_vertex_node[count][0] = (*it)[0];
34737 tmp_vector_vertex_node[count][1] = (*it)[1];
34738 tmp_vector_vertex_node[count][2] = (*it)[2];
34739 ++count;
34740 }
34741
34742 // Size of the vector
34743 unsigned n_vertex = tmp_vector_vertex_node.size();
34744
34745 // Tolerance below which the middle point can be deleted
34746 // (ratio of deflection to element length)
34747 double unrefinement_tolerance =
34748 open_polyline_pt->polyline_pt(p)->unrefinement_tolerance();
34749
34750 //------------------------------------------------------
34751 // Unrefinement
34752 //------------------------------------------------------
34753 if (unrefinement_tolerance > 0.0 && n_vertex >= 3)
34754 {
34755 unrefinement_was_performed = unrefine_boundary(bound,
34756 chunk,
34757 tmp_vector_vertex_node,
34758 unrefinement_tolerance,
34759 check_only);
34760
34761 // In this case the unrefinement_was_performed variable actually
34762 // tell us if the update had been performed when calling
34763 // with check_only=false
34764 if (check_only && unrefinement_was_performed)
34765 {
34766 // Cleanup (but only the elements -- the nodes still exist in
34767 // the bulk mesh!
34768 for (unsigned p = 0; p < n_polyline; p++)
34769 {
34770 face_mesh_pt[p]->flush_node_storage();
34771 delete face_mesh_pt[p];
34772 }
34773 return true;
34774 }
34775
34776 } // end of unrefinement
34777
34778 // Do not perform refinement if there are no more than two vertices
34779 // (open curve version)
34780 // New size of the vector
34781 n_vertex = tmp_vector_vertex_node.size();
34782
34783 //------------------------------------------------
34784 /// Refinement
34785 //------------------------------------------------
34786 double refinement_tolerance =
34787 open_polyline_pt->polyline_pt(p)->refinement_tolerance();
34788 if (refinement_tolerance > 0.0 && n_vertex >= 2)
34789 {
34790 refinement_was_performed = refine_boundary(face_mesh_pt[p],
34791 tmp_vector_vertex_node,
34792 refinement_tolerance,
34793 check_only);
34794
34795 // In this case the unrefinement_was_performed variable actually
34796 // tell us if the update had been performed when calling
34797 // with check_only=false
34798 if (check_only && refinement_was_performed)
34799 {
34800 // Cleanup (but only the elements -- the nodes still exist in
34801 // the bulk mesh!
34802 for (unsigned p = 0; p < n_polyline; p++)
34803 {
34804 face_mesh_pt[p]->flush_node_storage();
34805 delete face_mesh_pt[p];
34806 }
34807 return true;
34808 }
34809
34810 } // end refinement
34811
34812 // Do not perform maximum length constraint if there are no more than
34813 // two vertices
34814 // New size of the vector
34815 n_vertex = tmp_vector_vertex_node.size();
34816
34817 //------------------------------------------------
34818 // Maximum length constraint
34819 //-----------------------------------------------
34820 double maximum_length =
34821 open_polyline_pt->polyline_pt(p)->maximum_length();
34822 if (maximum_length > 0.0 && n_vertex >= 2)
34823 {
34824 bool max_length_applied = false;
34825 max_length_applied = apply_max_length_constraint(
34826 face_mesh_pt[p], tmp_vector_vertex_node, maximum_length);
34827
34828 // In this case the max length criteria was applied, check if
34829 // check_only=false
34830 if (check_only && max_length_applied)
34831 {
34832 // Cleanup (but only the elements -- the nodes still exist in
34833 // the bulk mesh!
34834 for (unsigned p = 0; p < n_polyline; p++)
34835 {
34836 face_mesh_pt[p]->flush_node_storage();
34837 delete face_mesh_pt[p];
34838 }
34839 return true;
34840 }
34841 }
34842
34843 // For further processing the three-dimensional vector
34844 // has to be reduced to a two-dimensional vector
34845 n_vertex = tmp_vector_vertex_node.size();
34846 Vector<Vector<double>> vector_vertex_node(n_vertex);
34847
34848 for (unsigned i = 0; i < n_vertex; i++)
34849 {
34850 vector_vertex_node[i].resize(2);
34851 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
34852 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
34853 }
34854
34855#ifdef OOMPH_HAS_MPI
34856 // Only perform this checking if the mesh is not distributed. When
34857 // the mesh is distributed the polylines continuity is addressed
34858 // in the sort_polylines_helper() method
34859 if (!this->is_mesh_distributed())
34860#endif
34861 {
34862 // Check whether the segments are continguous (first vertex of this
34863 // segment is equal to last vertex of previous segment).
34864 // If not, we should reverse the order of the current segment.
34865 // This check only applies for segments other than the first.
34866 // We only bother with this check, if we actually perform an update
34867 // of the polyline, i.e. if it's not only a check
34868 if ((p > 0) && !check_only)
34869 {
34870 // Final end point of previous line
34871 Vector<double> final_vertex_of_previous_segment;
34872 open_polyline_pt->polyline_pt(p - 1)->final_vertex_coordinate(
34873 final_vertex_of_previous_segment);
34874
34875 unsigned prev_seg_boundary_id =
34876 open_polyline_pt->curve_section_pt(p - 1)->boundary_id();
34877
34878 // Find the error between the final vertex of the previous
34879 // line and the first vertex of the current line
34880 double error = 0.0;
34881 for (unsigned i = 0; i < 2; i++)
34882 {
34883 const double dist = final_vertex_of_previous_segment[i] -
34884 (*vector_vertex_node.begin())[i];
34885 error += dist * dist;
34886 }
34887 error = sqrt(error);
34888
34889 // If the error is bigger than the tolerance then
34890 // we probably need to reverse, but better check
34892 {
34893 // Find the error between the final vertex of the previous
34894 // line and the first vertex of the current line
34895 error = 0.0;
34896 for (unsigned i = 0; i < 2; i++)
34897 {
34898 const double dist = final_vertex_of_previous_segment[i] -
34899 (*--vector_vertex_node.end())[i];
34900 error += dist * dist;
34901 }
34902 error = sqrt(error);
34903
34905 {
34906 // It could be possible that the first segment be reversed
34907 // and we did not notice it because this check does not
34908 // apply for the first segment. We can verify if the first
34909 // segment is reversed by using the vertex number 1
34910 if (p == 1)
34911 {
34912 // If no found it is possible that the previous polyline
34913 // be reversed Check for that case Initial point of
34914 // previous line
34915 Vector<double> initial_vertex_of_previous_segment;
34916 open_polyline_pt->polyline_pt(p - 1)->initial_vertex_coordinate(
34917 initial_vertex_of_previous_segment);
34918
34919 // Find the error between the initial vertex of the previous
34920 // line and the first vertex of the current line
34921 error = 0.0;
34922 for (unsigned i = 0; i < 2; i++)
34923 {
34924 const double dist = initial_vertex_of_previous_segment[i] -
34925 (*vector_vertex_node.begin())[i];
34926 error += dist * dist;
34927 }
34928 error = sqrt(error);
34929
34930 // If the error is bigger than the tolerance then
34931 // we probably need to reverse, but better check
34932 if (error >
34934 {
34935 // Find the error between the final vertex of the previous
34936 // line and the first vertex of the current line
34937 error = 0.0;
34938 for (unsigned i = 0; i < 2; i++)
34939 {
34940 const double dist = initial_vertex_of_previous_segment[i] -
34941 (*--vector_vertex_node.end())[i];
34942 error += dist * dist;
34943 }
34944 error = sqrt(error);
34945
34946 if (error >
34948 {
34949 std::ostringstream error_stream;
34950 error_stream
34951 << "The distance between the first node of the current\n"
34952 << "line segment (boundary " << bound
34953 << ") and either end of the previous line segment\n"
34954 << "(boundary " << prev_seg_boundary_id
34955 << ") is bigger than "
34956 << "the desired tolerance "
34958 << ".\n"
34959 << "This suggests that the polylines defining the open "
34960 << "curve\n"
34961 << "representation are not properly ordered.\n"
34962 << "Fail on last vertex of polyline: ("
34963 << prev_seg_boundary_id
34964 << ") and\nfirst vertex of polyline (" << bound << ").\n"
34965 << "This should have failed when first trying to "
34966 "construct\n"
34967 << "the open curve.\n";
34968 throw OomphLibError(error_stream.str(),
34969 OOMPH_CURRENT_FUNCTION,
34970 OOMPH_EXCEPTION_LOCATION);
34971 }
34972 else // We have to reverse both
34973 {
34974 // First reverse the previous polyline
34975 open_polyline_pt->polyline_pt(p - 1)->reverse();
34976 // Then reverse the current polyline
34977 std::reverse(vector_vertex_node.begin(),
34978 vector_vertex_node.end());
34979 }
34980 }
34981 else
34982 {
34983 // Reverse the previous polyline only
34984 open_polyline_pt->polyline_pt(p - 1)->reverse();
34985 }
34986 } // if (p == 1)
34987 else
34988 {
34989 std::ostringstream error_stream;
34990 error_stream
34991 << "The distance between the first node of the current\n"
34992 << "line segment (boundary " << bound
34993 << ") and either end of "
34994 << "the previous line segment\n"
34995 << "(boundary " << prev_seg_boundary_id
34996 << ") is bigger than the "
34997 << "desired tolerance "
34999 << ".\n"
35000 << "This suggests that the polylines defining the polygonal\n"
35001 << "representation are not properly ordered.\n"
35002 << "Fail on last vertex of polyline: ("
35003 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
35004 << bound << ").\n"
35005 << "This should have failed when first trying to construct "
35006 "the\n"
35007 << "polygon.\n";
35008 throw OomphLibError(error_stream.str(),
35009 OOMPH_CURRENT_FUNCTION,
35010 OOMPH_EXCEPTION_LOCATION);
35011 }
35012 }
35013 else
35014 {
35015 // Reverse the current vector to line up with the previous one
35016 std::reverse(vector_vertex_node.begin(),
35017 vector_vertex_node.end());
35018 }
35019 }
35020
35021 } // if p > 0
35022
35023 } // is mesh not distributed?
35024
35025 if (!check_only)
35026 {
35027 // Now update the polyline according to the new vertices The new
35028 // one representation
35029 TriangleMeshPolyLine* tmp_polyline =
35030 new TriangleMeshPolyLine(vector_vertex_node, bound);
35031
35032 // Create a temporal "curve section" version of the recently
35033 // created polyline
35034 TriangleMeshCurveSection* tmp_curve_section = tmp_polyline;
35035
35036 // Copy the unrefinement and refinement information
35037 tmp_polyline->set_unrefinement_tolerance(unrefinement_tolerance);
35038 tmp_polyline->set_refinement_tolerance(refinement_tolerance);
35039
35040 // Establish the maximum length constraint
35041 tmp_polyline->set_maximum_length(maximum_length);
35042
35043 // Pass the connection information from the old polyline to the
35044 // new one
35045 this->copy_connection_information(open_polyline_pt->polyline_pt(p),
35046 tmp_curve_section);
35047
35048 std::set<TriangleMeshCurveSection*>::iterator it =
35049 this->Free_curve_section_pt.find(
35050 open_polyline_pt->curve_section_pt(p));
35051
35052 bool delete_it_on_destructor = false;
35053
35054 if (it != this->Free_curve_section_pt.end())
35055 {
35056 // Free previous representation only if you created
35057 this->Free_curve_section_pt.erase(it);
35058 delete open_polyline_pt->curve_section_pt(p);
35059 delete_it_on_destructor = true;
35060 }
35061
35062 // *****************************************************************
35063 // Copying the new representation
35064 open_polyline_pt->curve_section_pt(p) = tmp_polyline;
35065
35066 // Update the Boundary <--> PolyLine map
35067 this->Boundary_curve_section_pt[bound] =
35068 open_polyline_pt->curve_section_pt(p);
35069
35070 if (delete_it_on_destructor)
35071 {
35072 this->Free_curve_section_pt.insert(
35073 open_polyline_pt->curve_section_pt(p));
35074 }
35075
35076 } // if(!check_only)
35077
35078 } // n_polylines
35079
35080 // Cleanup (but only the elements -- the nodes still exist in
35081 // the bulk mesh!
35082 for (unsigned p = 0; p < n_polyline; p++)
35083 {
35084 face_mesh_pt[p]->flush_node_storage();
35085 delete face_mesh_pt[p];
35086 }
35087
35088 if (check_only)
35089 {
35090 // if we end up all the way down here, no update of the internal
35091 // boundaries is necessary (in case we only check)
35092 return false;
35093 }
35094 else
35095 {
35096 // if we not only check, but actually perform the update and end
35097 // up all the way down here then we indicate whether an update was
35098 // performed or not
35099 return (unrefinement_was_performed || refinement_was_performed ||
35100 max_length_applied);
35101 }
35102 }
35103
35104 //=========================================================================
35105 /// Helper function that performs the unrefinement process
35106 /// on the specified boundary by using the provided vertices
35107 /// representation. Optional boolean is used to run it as test only (if
35108 /// true is specified as input) in which case vertex coordinates aren't
35109 /// actually modified. Returned boolean indicates if polyline was (or
35110 /// would have been -- if called with check_only=false) changed.
35111 //=========================================================================
35112 template<class ELEMENT>
35114 const unsigned& b,
35115 const unsigned& c,
35116 Vector<Vector<double>>& vector_bnd_vertices,
35117 double& unrefinement_tolerance,
35118 const bool& check_only)
35119 {
35120 // Store the vertices not allowed for deletion
35121 std::set<Vector<double>> no_delete_vertex;
35122
35123 // Does the boundary receives connections?
35124 const bool boundary_receive_connections =
35125 this->boundary_connections(b, c, no_delete_vertex);
35126
35127 // Boolean that indicates whether an actual update of the vertex
35128 // coordinates was performed or not
35129 bool unrefinement_was_performed = false;
35130
35131 unsigned n_vertex = vector_bnd_vertices.size();
35132
35133 // Initialise counter that indicates at which vertex we're currently
35134 // considering for deletion
35135 unsigned counter = 1;
35136
35137 // Loop over the nodes; start with the second one and increment by two
35138 // this way a "pack" of three nodes will be considered for calculation:
35139 // the middle-node (which is to be deleted or not) and the adjacent
35140 // nodes
35141 for (unsigned i = 1; i <= n_vertex - 2; i += 2)
35142 {
35143 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35144 double a_x = vector_bnd_vertices[i - 1][1];
35145 double a_y = vector_bnd_vertices[i - 1][2];
35146 double b_x = vector_bnd_vertices[i][1];
35147 double b_y = vector_bnd_vertices[i][2];
35148 double c_x = vector_bnd_vertices[i + 1][1];
35149 double c_y = vector_bnd_vertices[i + 1][2];
35150
35151 double a = b_x - a_x;
35152 double b = b_y - a_y;
35153 double c = c_x - a_x;
35154 double d = c_y - a_y;
35155
35156 double e = a * (a_x + b_x) + b * (a_y + b_y);
35157 double f = c * (a_x + c_x) + d * (a_y + c_y);
35158
35159 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35160
35161 bool do_it = false;
35162 if (std::fabs(g) < 1.0e-14)
35163 {
35164 do_it = true;
35165 if (check_only)
35166 {
35167 return true;
35168 }
35169 }
35170 else
35171 {
35172 double p_x = (d * e - b * f) / g;
35173 double p_y = (a * f - c * e) / g;
35174
35175 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35176
35177 double rhalfca_x = 0.5 * (a_x - c_x);
35178 double rhalfca_y = 0.5 * (a_y - c_y);
35179
35180 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35181
35182 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35183
35184 // If sticky out bit divided by distance between end nodes
35185 // is less than tolerance the boundary is so flat that we
35186 // can safely kill the node
35187 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35188 unrefinement_tolerance)
35189 {
35190 do_it = true;
35191 if (check_only)
35192 {
35193 return true;
35194 }
35195 }
35196 }
35197
35198 // If the vertex was proposed for deletion check that it is
35199 // allowed for being deleted
35200 if (do_it && boundary_receive_connections)
35201 {
35202 // Is the vertex one of the non deletable vertices
35203 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35204 it != no_delete_vertex.end();
35205 it++)
35206 {
35207 // Compute the distance between the proposed node to delete
35208 // and the ones that should not be deleted
35209 const double x = (*it)[0];
35210 const double y = (*it)[1];
35211 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35212 error = sqrt(error);
35213
35215 {
35216 // Do not delete the vertex
35217 do_it = false;
35218 break;
35219 }
35220 }
35221
35222 } // if (do_it && boundary_receive_connections)
35223
35224 // Remove node?
35225 if (do_it)
35226 {
35227 vector_bnd_vertices[i].resize(0);
35228 }
35229
35230 // Increase the counter, that indicates the number of the
35231 // next middle node
35232 counter += 2;
35233 }
35234
35235 // coming out of here the value of counter is the index of the
35236 // last node on the polyline counter=n_vertex-1 (in case of an
35237 // even number of nodes) or counter has the value of the number
35238 // of nodes on the polyline counter=n_vertex (in case of an odd
35239 // number of nodes
35240
35241 // Special treatment for the end of the polyline:
35242 // If the number of nodes is even, then the previous loop stopped
35243 // at the last but second node, i.e. the current value of counter
35244 // is the index of the last node. If that's the case, the last but
35245 // one node needs to be treated separately
35246 if ((counter) == (n_vertex - 1))
35247 {
35248 // Set the last but one node as middle node
35249 unsigned i = vector_bnd_vertices.size() - 2;
35250
35251 // Index of the current! last but second node (considering any
35252 // previous deletion)
35253 unsigned n = 0;
35254
35255 if (vector_bnd_vertices[counter - 2].size() != 0)
35256 {
35257 // if the initial last but second node does still exist then
35258 // this one is obviously also the current last but second one
35259 n = counter - 2;
35260 }
35261 else
35262 {
35263 // if the initial last but second node was deleted then the
35264 // initial last but third node is the current last but second
35265 // node
35266 n = counter - 3;
35267 }
35268
35269 // CODE DUPLICATION -- CAN'T BE BOTHERED TO WRITE A SEPARATE
35270 // FUNCTION FOR THIS; PROBABLY WORTH DOING IF/WHEN THERE'S
35271 // A MISTAKE IN ANY OF THIS AND IT NEEDS TO BE FIXED...
35272
35273 // Maths from http://www.cgafaq.info/wiki/Circle_Through_Three_Points
35274 double a_x = vector_bnd_vertices[n][1];
35275 double a_y = vector_bnd_vertices[n][2];
35276 double b_x = vector_bnd_vertices[i][1];
35277 double b_y = vector_bnd_vertices[i][2];
35278 double c_x = vector_bnd_vertices[i + 1][1];
35279 double c_y = vector_bnd_vertices[i + 1][2];
35280
35281 double a = b_x - a_x;
35282 double b = b_y - a_y;
35283 double c = c_x - a_x;
35284 double d = c_y - a_y;
35285
35286 double e = a * (a_x + b_x) + b * (a_y + b_y);
35287 double f = c * (a_x + c_x) + d * (a_y + c_y);
35288
35289 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
35290
35291 bool do_it = false;
35292 if (std::fabs(g) < 1.0e-14)
35293 {
35294 do_it = true;
35295 if (check_only)
35296 {
35297 return true;
35298 }
35299 }
35300 else
35301 {
35302 double p_x = (d * e - b * f) / g;
35303 double p_y = (a * f - c * e) / g;
35304
35305 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
35306
35307 double rhalfca_x = 0.5 * (a_x - c_x);
35308 double rhalfca_y = 0.5 * (a_y - c_y);
35309
35310 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
35311
35312 double sticky_out_bit = r - sqrt(std::fabs((r * r) - halfca_squared));
35313
35314 // If sticky out bit divided by distance between end nodes
35315 // is less than tolerance the boundary is so flat that we
35316 // can safely kill the node
35317 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
35318 unrefinement_tolerance)
35319 {
35320 do_it = true;
35321 if (check_only)
35322 {
35323 return true;
35324 }
35325 }
35326 }
35327
35328 // If the vertex was proposed for deletion check that it is
35329 // allowed for being deleted
35330 if (do_it && boundary_receive_connections)
35331 {
35332 // Is the vertex one of the non deletable vertices
35333 for (std::set<Vector<double>>::iterator it = no_delete_vertex.begin();
35334 it != no_delete_vertex.end();
35335 it++)
35336 {
35337 // Compute the distance between the proposed node to delete
35338 // and the ones that should not be deleted
35339 const double x = (*it)[0];
35340 const double y = (*it)[1];
35341 double error = (b_x - x) * (b_x - x) + (b_y - y) * (b_y - y);
35342 error = sqrt(error);
35343
35345 {
35346 // Do not delete the vertex
35347 do_it = false;
35348 break;
35349 }
35350 }
35351
35352 } // if (do_it && boundary_receive_connections)
35353
35354 // Remove node?
35355 if (do_it)
35356 {
35357 vector_bnd_vertices[i].resize(0);
35358 }
35359 }
35360
35361 // Create another vector, which will only contain entries of
35362 // nodes that still exist
35363 Vector<Vector<double>> compact_vector;
35364 compact_vector.reserve(n_vertex);
35365 for (unsigned i = 0; i < n_vertex; i++)
35366 {
35367 // If the entry was not deleted include it in the new vector
35368 if (vector_bnd_vertices[i].size() != 0)
35369 {
35370 compact_vector.push_back(vector_bnd_vertices[i]);
35371 }
35372 }
35373
35374 /// Get the size of the vector that now includes all remaining nodes
35375 n_vertex = compact_vector.size();
35376
35377 // If the size of the vector containing the remaining nodes is
35378 // different from the size of the vector before the unrefinement
35379 // routine (with the original nodes)
35380 // then the polyline was obviously updated
35381 if (n_vertex != vector_bnd_vertices.size())
35382 {
35383 unrefinement_was_performed = true;
35384 }
35385
35386 /// Copy back
35387 vector_bnd_vertices.resize(n_vertex);
35388 for (unsigned i = 0; i < n_vertex; i++)
35389 {
35390 vector_bnd_vertices[i].resize(3);
35391 vector_bnd_vertices[i][0] = compact_vector[i][0];
35392 vector_bnd_vertices[i][1] = compact_vector[i][1];
35393 vector_bnd_vertices[i][2] = compact_vector[i][2];
35394 }
35395
35396 return unrefinement_was_performed;
35397 }
35398
35399 //=========================================================================
35400 /// Helper function that performs the refinement process
35401 /// on the specified boundary by using the provided vertices
35402 /// representation. Optional boolean is used to run it as test only (if
35403 /// true is specified as input) in which case vertex coordinates aren't
35404 /// actually modified. Returned boolean indicates if polyline was (or
35405 /// would have been -- if called with check_only=false) changed.
35406 //=========================================================================
35407 template<class ELEMENT>
35409 Mesh* face_mesh_pt,
35410 Vector<Vector<double>>& vector_bnd_vertices,
35411 double& refinement_tolerance,
35412 const bool& check_only)
35413 {
35414 // Boolean that indicates whether an actual update of the vertex
35415 // coordinates was performed or not
35416 bool refinement_was_performed = false;
35417
35418 // Create a geometric object from the mesh to represent
35419 // the curvilinear boundary
35420 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
35421
35422 // Get the total number of current vertices
35423 unsigned n_vertex = vector_bnd_vertices.size();
35424
35425 // Create a new (temporary) vector for the nodes, so
35426 // that new nodes can be stored
35427 Vector<Vector<double>> extended_vector;
35428
35429 // Reserve memory space for twice the number of already
35430 // existing nodes (worst case)
35431 extended_vector.reserve(2 * n_vertex);
35432
35433 // Loop over the nodes until the last but one node
35434 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35435 {
35436 // Get local coordinate of "left" node
35437 double zeta_left = vector_bnd_vertices[inod][0];
35438
35439 // Get position vector of "left" node
35440 Vector<double> R_left(2);
35441 for (unsigned i = 0; i < 2; i++)
35442 {
35443 R_left[i] = vector_bnd_vertices[inod][i + 1];
35444 }
35445
35446 // Get local coordinate of "right" node
35447 double zeta_right = vector_bnd_vertices[inod + 1][0];
35448
35449 // Get position vector of "right" node
35450 Vector<double> R_right(2);
35451 for (unsigned i = 0; i < 2; i++)
35452 {
35453 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35454 }
35455
35456 // Get the boundary coordinate of the midpoint
35457 Vector<double> zeta_mid(1);
35458 zeta_mid[0] = 0.5 * (zeta_left + zeta_right);
35459
35460 // Get the position vector of the midpoint on the
35461 // curvilinear boundary
35462 Vector<double> R_mid(2);
35463 mesh_geom_obj_pt->position(zeta_mid, R_mid);
35464
35465 // Get the position vector of the midpoint on the straight
35466 // line connecting "left" and "right" node
35467 Vector<double> R_mid_polygon(2);
35468 for (unsigned i = 0; i < 2; i++)
35469 {
35470 R_mid_polygon[i] = 0.5 * (R_right[i] + R_left[i]);
35471 }
35472
35473 // Calculate the distance between the midpoint on the curvilinear
35474 // boundary and the midpoint on the straight line
35475 double distance =
35476 sqrt((R_mid[0] - R_mid_polygon[0]) * (R_mid[0] - R_mid_polygon[0]) +
35477 (R_mid[1] - R_mid_polygon[1]) * (R_mid[1] - R_mid_polygon[1]));
35478
35479 // Calculating the length of the straight line
35480 double length = sqrt((R_right[0] - R_left[0]) * (R_right[0] - R_left[0]) +
35481 (R_right[1] - R_left[1]) * (R_right[1] - R_left[1]));
35482
35483 // If the ratio of distance between the midpoints to the length
35484 // of the straight line is larger than the tolerance
35485 // specified for the criterion when points can be deleted,
35486 // create a new node and add it to the (temporary) vector
35487 if ((distance / length) > refinement_tolerance)
35488 {
35489 if (check_only)
35490 {
35491 // Delete the allocated memory for the geometric object
35492 // that represents the curvilinear boundary
35493 delete mesh_geom_obj_pt;
35494 return true;
35495 }
35496
35497 Vector<double> new_node(3);
35498 new_node[0] = zeta_mid[0];
35499 new_node[1] = R_mid[0];
35500 new_node[2] = R_mid[1];
35501
35502 // Include the "left" node in the new "temporary" vector
35503 extended_vector.push_back(vector_bnd_vertices[inod]);
35504
35505 // Include the new node as well
35506 extended_vector.push_back(new_node);
35507 }
35508 else
35509 {
35510 // Include the "left" node in the new "temporary" vector
35511 // and move on to the next node
35512 extended_vector.push_back(vector_bnd_vertices[inod]);
35513 }
35514 } // end of loop over nodes
35515
35516 // Add the last node to the vector
35517 extended_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
35518
35519 /// Get the size of the vector that now includes all added nodes
35520 n_vertex = extended_vector.size();
35521
35522 // If the size of the vector including the added nodes is
35523 // different from the size of the vector before the refinement
35524 // routine then the polyline was obviously updated
35525 if (n_vertex != vector_bnd_vertices.size())
35526 {
35527 refinement_was_performed = true;
35528 }
35529
35530 // Copy across
35531 vector_bnd_vertices.resize(n_vertex);
35532 for (unsigned i = 0; i < n_vertex; i++)
35533 {
35534 vector_bnd_vertices[i].resize(3);
35535 vector_bnd_vertices[i][0] = extended_vector[i][0];
35536 vector_bnd_vertices[i][1] = extended_vector[i][1];
35537 vector_bnd_vertices[i][2] = extended_vector[i][2];
35538 }
35539
35540 // Delete the allocated memory for the geometric object
35541 // that represents the curvilinear boundary
35542 delete mesh_geom_obj_pt;
35543
35544 return refinement_was_performed;
35545 }
35546
35547 //=========================================================================
35548 // Helper function that applies the maximum length constraint
35549 // when it was specified. This will increase the number of points in
35550 // the current curve section in case that any segment on it does not
35551 // fulfils the requirement
35552 //=========================================================================
35553 template<class ELEMENT>
35555 Mesh* face_mesh_pt,
35556 Vector<Vector<double>>& vector_bnd_vertices,
35557 double& max_length_constraint)
35558 {
35559 // Boolean that indicates whether an actual update of the vertex
35560 // coordinates was performed or not
35561 bool max_length_applied = false;
35562
35563 // Create a geometric object from the mesh to represent
35564 // the curvilinear boundary
35565 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
35566
35567 // Get the total number of current vertices
35568 unsigned n_vertex = vector_bnd_vertices.size();
35569
35570 // Create a new (temporary) vector for the nodes, so
35571 // that new nodes can be stored
35572 Vector<Vector<double>> extended_vector;
35573
35574 // Loop over the nodes until the last but one node
35575 for (unsigned inod = 0; inod < n_vertex - 1; inod++)
35576 {
35577 // Get local coordinate of "left" node
35578 double zeta_left = vector_bnd_vertices[inod][0];
35579
35580 // Get position vector of "left" node
35581 Vector<double> R_left(2);
35582 for (unsigned i = 0; i < 2; i++)
35583 {
35584 R_left[i] = vector_bnd_vertices[inod][i + 1];
35585 }
35586
35587 // Get local coordinate of "right" node
35588 double zeta_right = vector_bnd_vertices[inod + 1][0];
35589
35590 // Get position vector of "right" node
35591 Vector<double> R_right(2);
35592 for (unsigned i = 0; i < 2; i++)
35593 {
35594 R_right[i] = vector_bnd_vertices[inod + 1][i + 1];
35595 }
35596
35597 // Include the "left" node in the new "temporary" vector
35598 extended_vector.push_back(vector_bnd_vertices[inod]);
35599
35600 // Check whether the current distance between the left and right node
35601 // is longer than the specified constraint or not
35602 double length = std::fabs(zeta_right - zeta_left);
35603
35604 // Do we need to introduce new nodes?
35605 if (length > max_length_constraint)
35606 {
35607 double n_pts = length / max_length_constraint;
35608 // We only want the integer part
35609 unsigned n_points = static_cast<unsigned>(n_pts);
35610 double zeta_increment =
35611 (zeta_right - zeta_left) / ((double)n_points + 1);
35612
35613 Vector<double> zeta(1);
35614 // Create the n_points+1 points inside the segment
35615 for (unsigned s = 1; s < n_points + 1; s++)
35616 {
35617 // Get the coordinates
35618 zeta[0] = zeta_left + zeta_increment * double(s);
35619 Vector<double> vertex(2);
35620 mesh_geom_obj_pt->position(zeta, vertex);
35621
35622 // Create the new node
35623 Vector<double> new_node(3);
35624 new_node[0] = zeta[0];
35625 new_node[1] = vertex[0];
35626 new_node[2] = vertex[1];
35627
35628 // Include the new node
35629 extended_vector.push_back(new_node);
35630 }
35631 }
35632 }
35633
35634 // Add the last node to the vector
35635 extended_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
35636
35637 /// Get the size of the vector that now includes all added nodes
35638 n_vertex = extended_vector.size();
35639
35640 // If the size of the vector including the added nodes is
35641 // different from the size of the vector before applying the maximum length
35642 // constraint then the polyline was obviously updated
35643 if (n_vertex != vector_bnd_vertices.size())
35644 {
35645 max_length_applied = true;
35646 }
35647
35648 // Copy across
35649 vector_bnd_vertices.resize(n_vertex);
35650 for (unsigned i = 0; i < n_vertex; i++)
35651 {
35652 vector_bnd_vertices[i].resize(3);
35653 vector_bnd_vertices[i][0] = extended_vector[i][0];
35654 vector_bnd_vertices[i][1] = extended_vector[i][1];
35655 vector_bnd_vertices[i][2] = extended_vector[i][2];
35656 }
35657
35658 // Delete the allocated memory for the geometric object
35659 // that represents the curvilinear boundary
35660 delete mesh_geom_obj_pt;
35661
35662 return max_length_applied;
35663 }
35664
35665 //=========================================================================
35666 /// Helper function
35667 /// Creates an unsorted face mesh representation from the specified
35668 /// boundary id. It means that the elements are not sorted along the
35669 /// boundary
35670 //=========================================================================
35671 template<class ELEMENT>
35673 create_unsorted_face_mesh_representation(const unsigned& boundary_id,
35674 Mesh* face_mesh_pt)
35675 {
35676 // Create a face mesh adjacent to specified boundary.
35677 // The face mesh consists of FaceElements that may also be
35678 // interpreted as GeomObjects
35679
35680 // Build the face mesh
35681 this->template build_face_mesh<ELEMENT, FaceElementAsGeomObject>(
35682 boundary_id, face_mesh_pt);
35683
35684 // Find the total number of added elements
35685 unsigned n_element = face_mesh_pt->nelement();
35686 // Loop over the elements
35687 for (unsigned e = 0; e < n_element; e++)
35688 {
35689 // Cast the element pointer to the correct thing!
35691 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
35692 face_mesh_pt->element_pt(e));
35693
35694 // Set bulk boundary number
35695 el_pt->set_boundary_number_in_bulk_mesh(boundary_id);
35696 }
35697 }
35698
35699 //=========================================================================
35700 /// Helper function
35701 /// Creates a sorted face mesh representation of the specified PolyLine
35702 /// It means that the elements are sorted along the boundary
35703 //=========================================================================
35704 template<class ELEMENT>
35706 const unsigned& boundary_id,
35707 Mesh* face_mesh_pt,
35708 std::map<FiniteElement*, bool>& is_inverted,
35709 bool& inverted_face_mesh)
35710 {
35711 Mesh* tmp_unsorted_face_mesh_pt = new Mesh();
35712
35713 // First step we get the unsorted version of the face mesh
35714 create_unsorted_face_mesh_representation(boundary_id,
35715 tmp_unsorted_face_mesh_pt);
35716
35717 // Once with the unsorted version of the face mesh
35718 // only left to sort it out!!!
35719
35720 // Put all face elements in order
35721 //-------------------------------
35722
35723 // Put first element into ordered list
35724 // Temporal list for sorting the elements
35725 std::list<FiniteElement*> sorted_el_pt;
35726 FiniteElement* el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(0);
35727 sorted_el_pt.push_back(el_pt);
35728
35729 // Number of nodes
35730 unsigned nnod = el_pt->nnode();
35731
35732 // Count elements that have been done
35733 unsigned count_done = 0;
35734
35735 // How many face elements are there?
35736 unsigned n_face_element = tmp_unsorted_face_mesh_pt->nelement();
35737
35738 // Keep track of who's done
35739 std::map<FiniteElement*, bool> done_el;
35740
35741 is_inverted.clear();
35742
35743 // Fit in the other elements in at most nel^2 loops
35744 for (unsigned ee = 1; ee < n_face_element; ee++)
35745 {
35746 // Loop over all elements to check if they fit to the right
35747 // or the left of the current one
35748 for (unsigned e = 1; e < n_face_element; e++)
35749 {
35750 // Candidate element
35751 el_pt = tmp_unsorted_face_mesh_pt->finite_element_pt(e);
35752
35753 // Is it done yet?
35754 if (!done_el[el_pt])
35755 {
35756 // Left and rightmost elements
35757 FiniteElement* first_el_pt = (*sorted_el_pt.begin());
35758 std::list<FiniteElement*>::iterator it = sorted_el_pt.end();
35759 it--;
35760 FiniteElement* last_el_pt = *it;
35761
35762 // Left and rightmost nodes
35763 Node* left_node_pt = first_el_pt->node_pt(0);
35764 if (is_inverted[first_el_pt])
35765 {
35766 left_node_pt = first_el_pt->node_pt(nnod - 1);
35767 }
35768 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
35769 if (is_inverted[last_el_pt])
35770 {
35771 right_node_pt = last_el_pt->node_pt(0);
35772 }
35773
35774 // New element fits at the left of first element and is not inverted
35775 if (left_node_pt == el_pt->node_pt(nnod - 1))
35776 {
35777 sorted_el_pt.push_front(el_pt);
35778 done_el[el_pt] = true;
35779 count_done++;
35780 is_inverted[el_pt] = false;
35781 }
35782 // New element fits at the left of first element and is inverted
35783
35784 else if (left_node_pt == el_pt->node_pt(0))
35785 {
35786 sorted_el_pt.push_front(el_pt);
35787 done_el[el_pt] = true;
35788 count_done++;
35789 is_inverted[el_pt] = true;
35790 }
35791 // New element fits on the right of last element and is not inverted
35792
35793 else if (right_node_pt == el_pt->node_pt(0))
35794 {
35795 sorted_el_pt.push_back(el_pt);
35796 done_el[el_pt] = true;
35797 count_done++;
35798 is_inverted[el_pt] = false;
35799 }
35800 // New element fits on the right of last element and is inverted
35801
35802 else if (right_node_pt == el_pt->node_pt(nnod - 1))
35803 {
35804 sorted_el_pt.push_back(el_pt);
35805 done_el[el_pt] = true;
35806 count_done++;
35807 is_inverted[el_pt] = true;
35808 }
35809
35810 if (done_el[el_pt])
35811 {
35812 break;
35813 }
35814 }
35815 }
35816 }
35817
35818 // Are we done?
35819 if (count_done != (n_face_element - 1))
35820 {
35821 std::ostringstream error_message;
35822 error_message << "When ordering FaceElements on "
35823 << "boundary " << boundary_id << " only managed to order \n"
35824 << count_done << " of " << n_face_element
35825 << " face elements.\n"
35826 << std::endl;
35827 throw OomphLibError(
35828 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
35829 }
35830
35831 // Now make a mesh that contains the FaceElements in order
35832 // Remember that we currently have a list, not a mesh of sorted elements
35833
35834 // Fill it
35835 for (std::list<FiniteElement*>::iterator it = sorted_el_pt.begin();
35836 it != sorted_el_pt.end();
35837 it++)
35838 {
35839 // Get element
35840 FiniteElement* el_pt = *it;
35841
35842 // add this face element to the order original mesh
35843 face_mesh_pt->add_element_pt(el_pt);
35844 }
35845
35846 // Verify if face mesh representation is not inverted according to the
35847 // polyline specified by the user, it means that the initial and the
35848 // final vertex does really correspond to the first and last vertex
35849 // respectively, if not, state that the face mesh representation is
35850 // inverted
35851
35852 // Get the associated polyline representation to the boundary
35853 TriangleMeshPolyLine* bnd_polyline =
35854 this->Boundary_curve_section_pt[boundary_id];
35855
35856 // Get the really first vertex
35857 Vector<double> first_vertex = bnd_polyline->vertex_coordinate(0);
35858
35859 // Now get the first node based on the face mesh representation
35860 // First get access to the first element
35861 FiniteElement* first_el_pt = face_mesh_pt->finite_element_pt(0);
35862
35863 // Now get access to the first node
35864 unsigned n_node = first_el_pt->nnode();
35865 // Get the very first node (taking into account if it is
35866 // inverted or not!!)
35867 Node* first_node_pt = first_el_pt->node_pt(0);
35868 if (is_inverted[first_el_pt])
35869 {
35870 first_node_pt = first_el_pt->node_pt(n_node - 1);
35871 }
35872
35873 double error = (first_node_pt->x(0) - first_vertex[0]) *
35874 (first_node_pt->x(0) - first_vertex[0]) +
35875 (first_node_pt->x(1) - first_vertex[1]) *
35876 (first_node_pt->x(1) - first_vertex[1]);
35877
35878 error = sqrt(error);
35879
35881 {
35882 inverted_face_mesh = false;
35883 }
35884 else
35885 {
35886 inverted_face_mesh = true;
35887 }
35888 }
35889
35890 //=========================================================================
35891 /// Helper function to construct face mesh representation of all polylines,
35892 /// possibly with segments re-distributed between polylines
35893 /// to maintain an approximately even sub-division of the polygon
35894 //=========================================================================
35895 template<class ELEMENT>
35897 TriangleMeshPolygon* polygon_pt, Vector<Mesh*>& face_mesh_pt)
35898 {
35899 // Number of polylines
35900 unsigned n_polyline = polygon_pt->npolyline();
35901 face_mesh_pt.resize(n_polyline);
35902
35903 // Are we eligible for re-distributing polyline segments between
35904 // polylines? We're not if any of the boundaries are associated
35905 // with a GeomObject because we're then tied to the start and
35906 // end coordinates along it.
35907 bool eligible_for_segment_redistribution = true;
35908
35909 // Loop over constituent polylines
35910 for (unsigned p = 0; p < n_polyline; p++)
35911 {
35912 // Get the boundary id of the polyline
35913 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35914
35915 // If the boundary has a geometric object representation then
35916 // we can't redistribute
35917 GeomObject* const geom_object_pt = this->boundary_geom_object_pt(bound);
35918 if (geom_object_pt != 0)
35919 {
35920 eligible_for_segment_redistribution = false;
35921 }
35922
35923 face_mesh_pt[p] = new Mesh();
35924 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
35925 }
35926
35928 {
35929 return;
35930 }
35931
35932 // If there is more than one region we have to think... Die for now.
35933 if (this->nregion() > 1)
35934 {
35935 std::ostringstream warn_message;
35936 warn_message
35937 << "Can't currently re-distribute segments between polylines if there\n"
35938 << "are multiple regions; returning..." << std::endl;
35939 OomphLibWarning(warn_message.str(),
35940 "RefineableTriangleMesh::get_face_mesh_representation()",
35941 OOMPH_EXCEPTION_LOCATION);
35942 return;
35943 }
35944
35945 // Redistribution overruled
35946 if (!eligible_for_segment_redistribution)
35947 {
35948 std::ostringstream warn_message;
35949 warn_message
35950 << "Over-ruling re-distribution of segments between polylines\n"
35951 << "because at least one boundary is associated with a GeomObject."
35952 << "Returning..." << std::endl;
35953 OomphLibWarning(warn_message.str(),
35954 "RefineableTriangleMesh::get_face_mesh_representation()",
35955 OOMPH_EXCEPTION_LOCATION);
35956 return;
35957 }
35958
35959 // Create a vector for ordered face mesh
35960 Vector<Mesh*> ordered_face_mesh_pt(n_polyline);
35961
35962 // Storage for the total arclength of polygon
35963 double s_total = 0.0;
35964
35965 // Storage for first and last nodes on polylines so we can figure
35966 // out if they are inverted relative to each other
35967 Vector<Node*> first_polyline_node_pt(n_polyline);
35968 Vector<Node*> last_polyline_node_pt(n_polyline);
35969 std::vector<bool> is_reversed(n_polyline, false);
35970
35971 // Loop over constituent polylines
35972 for (unsigned p = 0; p < n_polyline; p++)
35973 {
35974 // Put all face elements in order
35975 //-------------------------------
35976
35977 // Put first element into ordered list
35978 std::list<FiniteElement*> ordered_el_pt;
35979 FiniteElement* el_pt = face_mesh_pt[p]->finite_element_pt(0);
35980 ordered_el_pt.push_back(el_pt);
35981
35982 // Number of nodes
35983 unsigned nnod = el_pt->nnode();
35984
35985 // Default for first and last node on polyline
35986 first_polyline_node_pt[p] = el_pt->node_pt(0);
35987 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
35988
35989 // Count elements that have been done
35990 unsigned count_done = 0;
35991
35992 // How many face elements are there?
35993 unsigned n_face_element = face_mesh_pt[p]->nelement();
35994
35995 // Get the boundary id of the polyline
35996 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
35997
35998 // Keep track of who's done
35999 std::map<FiniteElement*, bool> done_el;
36000
36001 // Keep track of which element is inverted
36002 std::map<FiniteElement*, bool> is_inverted;
36003
36004 // Fit in the other elements in at most nel^2 loops
36005 for (unsigned ee = 1; ee < n_face_element; ee++)
36006 {
36007 // Loop over all elements to check if they fit to the right
36008 // or the left of the current one
36009 for (unsigned e = 1; e < n_face_element; e++)
36010 {
36011 // Candidate element
36012 el_pt = face_mesh_pt[p]->finite_element_pt(e);
36013
36014 // Is it done yet?
36015 if (!done_el[el_pt])
36016 {
36017 // Left and rightmost elements
36018 FiniteElement* first_el_pt = (*ordered_el_pt.begin());
36019 std::list<FiniteElement*>::iterator it = ordered_el_pt.end();
36020 it--;
36021 FiniteElement* last_el_pt = *it;
36022
36023 // Left and rightmost nodes
36024 Node* left_node_pt = first_el_pt->node_pt(0);
36025 if (is_inverted[first_el_pt])
36026 {
36027 left_node_pt = first_el_pt->node_pt(nnod - 1);
36028 }
36029 Node* right_node_pt = last_el_pt->node_pt(nnod - 1);
36030 if (is_inverted[last_el_pt])
36031 {
36032 right_node_pt = last_el_pt->node_pt(0);
36033 }
36034
36035 // New element fits at the left of first element and is not inverted
36036 if (left_node_pt == el_pt->node_pt(nnod - 1))
36037 {
36038 ordered_el_pt.push_front(el_pt);
36039 done_el[el_pt] = true;
36040 count_done++;
36041 is_inverted[el_pt] = false;
36042 first_polyline_node_pt[p] = el_pt->node_pt(0);
36043 }
36044 // New element fits at the left of first element and is inverted
36045
36046 else if (left_node_pt == el_pt->node_pt(0))
36047 {
36048 ordered_el_pt.push_front(el_pt);
36049 done_el[el_pt] = true;
36050 count_done++;
36051 is_inverted[el_pt] = true;
36052 first_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
36053 }
36054 // New element fits on the right of last element and is not inverted
36055
36056 else if (right_node_pt == el_pt->node_pt(0))
36057 {
36058 ordered_el_pt.push_back(el_pt);
36059 done_el[el_pt] = true;
36060 count_done++;
36061 is_inverted[el_pt] = false;
36062 last_polyline_node_pt[p] = el_pt->node_pt(nnod - 1);
36063 }
36064 // New element fits on the right of last element and is inverted
36065
36066 else if (right_node_pt == el_pt->node_pt(nnod - 1))
36067 {
36068 ordered_el_pt.push_back(el_pt);
36069 done_el[el_pt] = true;
36070 count_done++;
36071 is_inverted[el_pt] = true;
36072 last_polyline_node_pt[p] = el_pt->node_pt(0);
36073 }
36074
36075 if (done_el[el_pt])
36076 {
36077 break;
36078 }
36079 }
36080 }
36081 }
36082
36083 // Are we done?
36084 if (count_done != (n_face_element - 1))
36085 {
36086 std::ostringstream error_message;
36087 error_message << "When ordering FaceElements on "
36088 << "boundary " << bound << " only managed to order \n"
36089 << count_done << " of " << n_face_element
36090 << " face elements.\n"
36091 << std::endl;
36092 throw OomphLibError(error_message.str(),
36093 OOMPH_CURRENT_FUNCTION,
36094 OOMPH_EXCEPTION_LOCATION);
36095 }
36096
36097 // Now make a mesh that contains the FaceElements in order
36098 ordered_face_mesh_pt[p] = new Mesh;
36099
36100 // Fill it
36101 for (std::list<FiniteElement*>::iterator it = ordered_el_pt.begin();
36102 it != ordered_el_pt.end();
36103 it++)
36104 {
36105 // Get element
36106 FiniteElement* el_pt = *it;
36107
36108 // add this face element to the order original mesh
36109 ordered_face_mesh_pt[p]->add_element_pt(el_pt);
36110 }
36111
36112 // Get the arclength along the polygon
36113 for (unsigned e = 0; e < n_face_element; ++e)
36114 {
36115 FiniteElement* el_pt = ordered_face_mesh_pt[p]->finite_element_pt(e);
36116 unsigned n_node = el_pt->nnode();
36117 double element_length_squared = 0.0;
36118 for (unsigned i = 0; i < 2; i++)
36119 {
36120 element_length_squared +=
36121 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36122 }
36123
36124 // Determine element length
36125 double element_length = sqrt(element_length_squared);
36126
36127 // Add this length to the total arclength
36128 s_total += element_length;
36129 }
36130
36131 // Empty the original meshes
36132 face_mesh_pt[p]->flush_element_and_node_storage();
36133 }
36134
36135 // Is first one reversed?
36136 if ((last_polyline_node_pt[0] == first_polyline_node_pt[1]) ||
36137 (last_polyline_node_pt[0] == last_polyline_node_pt[1]))
36138 {
36139 is_reversed[0] = false;
36140 }
36141 else if ((first_polyline_node_pt[0] == first_polyline_node_pt[1]) ||
36142 (first_polyline_node_pt[0] == last_polyline_node_pt[1]))
36143 {
36144 is_reversed[0] = true;
36145 }
36146
36147 // Reorder the face meshes so that they are contiguous
36148 Vector<Mesh*> tmp_face_mesh_pt(n_polyline);
36149 std::vector<bool> mesh_done(n_polyline, false);
36150 Vector<unsigned> old_polyline_number(n_polyline);
36151
36152 // Initial entry
36153 tmp_face_mesh_pt[0] = ordered_face_mesh_pt[0];
36154 unsigned current = 0;
36155 old_polyline_number[0] = 0;
36156 unsigned count_found = 0;
36157
36158 // Fill in the next entries
36159 for (unsigned p = 1; p < n_polyline; p++)
36160 {
36161 Node* end_node_pt = last_polyline_node_pt[current];
36162 if (is_reversed[current])
36163 {
36164 end_node_pt = first_polyline_node_pt[current];
36165 }
36166
36167 // Loop over all remaining face meshes to see which one fits
36168 for (unsigned pp = 1; pp < n_polyline; pp++)
36169 {
36170 if (!mesh_done[pp])
36171 {
36172 // Current one is not reversed, candidate is not reversed
36173 if ((!is_reversed[current]) &&
36174 (end_node_pt == first_polyline_node_pt[pp]))
36175 {
36176 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36177 mesh_done[pp] = true;
36178 is_reversed[pp] = false;
36179 old_polyline_number[p] = pp;
36180 current = pp;
36181 count_found++;
36182 break;
36183 }
36184 // Current one is not reversed, candidate is reversed
36185
36186 else if ((!is_reversed[current]) &&
36187 (end_node_pt == last_polyline_node_pt[pp]))
36188 {
36189 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36190 mesh_done[pp] = true;
36191 is_reversed[pp] = true;
36192 old_polyline_number[p] = pp;
36193 current = pp;
36194 count_found++;
36195 break;
36196 }
36197 // Current one is reversed, candidate is not reversed
36198
36199 else if ((is_reversed[current]) &&
36200 (end_node_pt == first_polyline_node_pt[pp]))
36201 {
36202 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36203 mesh_done[pp] = true;
36204 is_reversed[pp] = false;
36205 old_polyline_number[p] = pp;
36206 current = pp;
36207 count_found++;
36208 break;
36209 }
36210 // Current one is reversed, candidate is reversed
36211
36212 else if ((is_reversed[current]) &&
36213 (end_node_pt == last_polyline_node_pt[pp]))
36214 {
36215 tmp_face_mesh_pt[p] = ordered_face_mesh_pt[pp];
36216 mesh_done[pp] = true;
36217 is_reversed[pp] = true;
36218 old_polyline_number[p] = pp;
36219 current = pp;
36220 count_found++;
36221 break;
36222 }
36223 }
36224 }
36225 }
36226
36227#ifdef PARANOID
36228 if (count_found != n_polyline - 1)
36229 {
36230 std::ostringstream error_message;
36231 error_message << "Only found " << count_found << " out of "
36232 << n_polyline - 1 << " polylines to be fitted in.\n";
36233 throw OomphLibError(
36234 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36235 }
36236#endif
36237
36238 // Now overwrite the re-ordered data
36239 for (unsigned i = 0; i < n_polyline; i++)
36240 {
36241 ordered_face_mesh_pt[i] = tmp_face_mesh_pt[i];
36242 }
36243
36244 // Now do an approximate equidistribution of polylines
36245 //----------------------------------------------------
36246 double s = 0.0;
36247 unsigned new_face_id = 0;
36248
36249 // Matrix map to indicate if node must not be removed from specified
36250 // boundary (!=0) or not (=0). Initialises itself to zero
36251 std::map<Node*, std::map<unsigned, unsigned>>
36252 node_must_not_be_removed_from_boundary_flag;
36253
36254 // Loop over the old face mesh
36255 for (unsigned p = 0; p < n_polyline; p++)
36256 {
36257 // Loop over the face elements
36258 unsigned n_face_element = ordered_face_mesh_pt[p]->nelement();
36259 for (unsigned e = 0; e < n_face_element; e++)
36260 {
36261 unsigned el_number = e;
36262 if (is_reversed[p])
36263 {
36264 el_number = n_face_element - e - 1;
36265 }
36266
36267 FiniteElement* el_pt =
36268 ordered_face_mesh_pt[p]->finite_element_pt(el_number);
36269 unsigned n_node = el_pt->nnode();
36270
36271 // Determine element length
36272 double element_length_squared = 0.0;
36273 for (unsigned i = 0; i < 2; i++)
36274 {
36275 element_length_squared +=
36276 pow(el_pt->node_pt(n_node - 1)->x(i) - el_pt->node_pt(0)->x(i), 2);
36277 }
36278 double element_length = sqrt(element_length_squared);
36279
36280 // Add this length to the total arclength
36281 s += element_length;
36282
36283 // Check if the current 'arclength' is less than the
36284 // whole 'arclength' divided by the number of polylines
36285 if (s < s_total / double(n_polyline) + 1e-6)
36286 {
36287 // If so add this face element to the new face mesh
36288 face_mesh_pt[new_face_id]->add_element_pt(el_pt);
36289
36290 unsigned bound_old =
36291 polygon_pt->polyline_pt(old_polyline_number[p])->boundary_id();
36292
36293 unsigned bound_new =
36294 polygon_pt->polyline_pt(new_face_id)->boundary_id();
36295
36296 // Loop over the nodes in the element
36297 for (unsigned i = 0; i < n_node; i++)
36298 {
36299 // Get the pointer to the node
36300 Node* nod_pt = el_pt->node_pt(i);
36301
36302 // If the two boundary id's are different, the face element's nodes
36303 // have to be added to the new boundary
36304 if (bound_new != bound_old)
36305 {
36306 // Add it to the new boundary
36307 add_boundary_node(bound_new, nod_pt);
36308
36309 // We are happy for this node to be removed from the
36310 // old boundary?
36311 node_must_not_be_removed_from_boundary_flag[nod_pt][bound_old] +=
36312 0;
36313 }
36314
36315 // If the face element hasn't moved, its nodes MUST remain
36316 // on that boundary (incl. any nodes that ar shared by
36317 // FaceElements that have moved (see above)
36318
36319 else
36320 {
36321 node_must_not_be_removed_from_boundary_flag[nod_pt][bound_old] +=
36322 1;
36323 }
36324 }
36325 }
36326
36327 // If not, reset the current 'arclength' to zero,
36328 // increase the new face id by one and go one element
36329 // back by decreasing e by one to make sure the current
36330 // element gets added to the next face mesh
36331
36332 else
36333 {
36334 if (new_face_id != n_polyline - 1)
36335 {
36336 s = 0.0;
36337 new_face_id++;
36338 --e;
36339 }
36340 else
36341 {
36342 s = 0.0;
36343 --e;
36344 }
36345 }
36346 }
36347 } // end of loop over all polylines -- they are now re-distributed
36348
36349
36350 // Loop over all nodes on the boundaries of the polygon to remove
36351 // nodes from boundaries they are no longer on
36352 unsigned move_count = 0;
36353 for (std::map<Node*, std::map<unsigned, unsigned>>::iterator it =
36354 node_must_not_be_removed_from_boundary_flag.begin();
36355 it != node_must_not_be_removed_from_boundary_flag.end();
36356 it++)
36357 {
36358 // Get the node
36359 Node* nod_pt = (*it).first;
36360
36361 // Now we loop over the boundaries that this node is on
36362 for (std::map<unsigned, unsigned>::iterator it_2 = (*it).second.begin();
36363 it_2 != (*it).second.end();
36364 it_2++)
36365 {
36366 // Get the boundary id
36367 unsigned bound = (*it_2).first;
36368
36369 // Remove it from that boundary?
36370 if ((*it_2).second == 0)
36371 {
36372 remove_boundary_node(bound, nod_pt);
36373 move_count++;
36374 }
36375 }
36376 }
36377
36378 // Loop over the new face mesh to assign new boundary IDs
36379 for (unsigned p = 0; p < n_polyline; p++)
36380 {
36381 // Get the boundary id of the polyline
36382 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36383
36384 // Loop over the face elements
36385 unsigned n_face_element = face_mesh_pt[p]->nelement();
36386 for (unsigned e = 0; e < n_face_element; e++)
36387 {
36388 // Cast the element pointer to the correct thing!
36390 dynamic_cast<FaceElementAsGeomObject<ELEMENT>*>(
36391 face_mesh_pt[p]->element_pt(e));
36392
36393 // Set bulk boundary number
36395 }
36396 }
36397
36398 // Update look-up for elements next to boundary
36399 setup_boundary_element_info();
36400
36401 // Now re-create the boundary coordinates
36402 for (unsigned p = 0; p < n_polyline; p++)
36403 {
36404 // Get the boundary id of the polyline
36405 unsigned bound = polygon_pt->polyline_pt(p)->boundary_id();
36406
36407 // Do it
36408 this->template setup_boundary_coordinates<ELEMENT>(bound);
36409 }
36410
36411 // Clean up
36412 for (unsigned p = 0; p < n_polyline; p++)
36413 {
36414 // Flush the nodes from the face mesh to make sure we
36415 // don't delete them (the face mesh that we're returning from here
36416 // still needs them!)
36417 ordered_face_mesh_pt[p]->flush_element_and_node_storage();
36418 delete ordered_face_mesh_pt[p];
36419 }
36420 }
36421
36422 //=========================================================================
36423 /// Helper function to construct face mesh representation of all polylines
36424 //=========================================================================
36425 template<class ELEMENT>
36427 TriangleMeshOpenCurve* open_polyline_pt, Vector<Mesh*>& face_mesh_pt)
36428 {
36429 // Number of polylines
36430 unsigned n_polyline = open_polyline_pt->ncurve_section();
36431 face_mesh_pt.resize(n_polyline);
36432
36433 // Loop over constituent polylines
36434 for (unsigned p = 0; p < n_polyline; p++)
36435 {
36436 // Get the boundary id of the polyline
36437 unsigned bound = open_polyline_pt->curve_section_pt(p)->boundary_id();
36438
36439 face_mesh_pt[p] = new Mesh();
36440 create_unsorted_face_mesh_representation(bound, face_mesh_pt[p]);
36441 }
36442 }
36443
36444 //======================================================================
36445 /// Update the PSLG that define the inner boundaries of the mesh.
36446 /// Optional boolean is used to run it as test only (if
36447 /// true is specified as input) in which case PSLG isn't actually
36448 /// modified. Returned boolean indicates if PSLG was (or would have
36449 /// been -- if called with check_only=false) changed.
36450 //======================================================================
36451 template<class ELEMENT>
36453 ELEMENT>::surface_remesh_for_inner_hole_boundaries(Vector<Vector<double>>&
36454 internal_point_coord,
36455 const bool& check_only)
36456 {
36457 // Boolean to indicate whether an actual update of the internal
36458 // holes was performed
36459 bool update_was_performed = false;
36460 // Loop over the number of internal boundaries
36461 unsigned n_hole = internal_point_coord.size();
36462 for (unsigned ihole = 0; ihole < n_hole; ihole++)
36463 {
36464 // Cache the pointer to the polygon representation
36465 TriangleMeshPolygon* const poly_pt = this->Internal_polygon_pt[ihole];
36466
36467
36468 // Can the polygon update its own configuration, in which case this
36469 // is easy
36471 {
36473
36474 // Initialize Vector hole_coordinates
36475 internal_point_coord[ihole].resize(2);
36476
36477 // Get the vector of hole coordinates
36478 internal_point_coord[ihole] = poly_pt->internal_point();
36479 }
36480 // Otherwise we have to work much harder
36481
36482 else
36483 {
36484 // if we only want to check whether an update of the inner
36485 // hole is necessary
36486 if (check_only)
36487 {
36488 // is it necessary?
36489 bool update_necessary =
36490 this->update_polygon_using_face_mesh(poly_pt, check_only);
36491
36492 // Yes?
36493 if (update_necessary)
36494 {
36495 // then we have to adaptand return 'true'
36496 return true;
36497 }
36498 }
36499 // if we not only want to check, then we actually perform
36500 // the update
36501 else
36502 {
36503 update_was_performed = this->update_polygon_using_face_mesh(poly_pt);
36504 }
36505
36506 // Now we need to sort out the hole coordinates
36507 if (!poly_pt->internal_point().empty())
36508 {
36509 // If fixed don't update and simply
36510 // Read out the existing value
36511 if (poly_pt->is_internal_point_fixed())
36512 {
36513 // Get the vector of hole coordinates
36514 internal_point_coord[ihole] = poly_pt->internal_point();
36515 }
36516 // This is where the work starts and this could be made much
36517 // better than the current hack
36518 else
36519 {
36520 // If the user has set their own function then use that
36521 if (this->Internal_hole_point_update_fct_pt != 0)
36522 {
36523 this->Internal_hole_point_update_fct_pt(ihole, poly_pt);
36524 }
36525 // Otherwise use our clunky default
36526 else
36527 {
36528 // Now sort out the hole coordinates
36529 Vector<double> vertex_coord;
36530 unsigned n_polyline = poly_pt->npolyline();
36531
36532 // Initialize Vector hole_coordinates
36533 vertex_coord.resize(2);
36534 internal_point_coord[ihole].resize(2);
36535
36536 // Hole centre will be found by averaging the position of
36537 // all vertex nodes
36538 internal_point_coord[ihole][0] = 0.0;
36539 internal_point_coord[ihole][1] = 0.0;
36540
36541 for (unsigned p = 0; p < n_polyline; p++)
36542 {
36543 Vector<double> poly_ave(2, 0.0);
36544 // How many vertices are there in the segment
36545 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36546 for (unsigned v = 0; v < n_vertex; v++)
36547 {
36548 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36549 for (unsigned i = 0; i < 2; i++)
36550 {
36551 poly_ave[i] += vertex_coord[i];
36552 }
36553 }
36554
36555 // Add the average polyline coordinate to the hole centre
36556 for (unsigned i = 0; i < 2; i++)
36557 {
36558 internal_point_coord[ihole][i] += poly_ave[i] / n_vertex;
36559 }
36560 }
36561
36562 // Now average out the hole centre
36563 for (unsigned i = 0; i < 2; i++)
36564 {
36565 internal_point_coord[ihole][i] /= n_polyline;
36566 }
36567
36568 // We have now found the hole centre stored in
36569 // internal_point_coordinate[ihole][i]
36570
36571 // Find polylines that intersect at y average value
36572 // Alice's version but this does not work if the end point of a
36573 // segment is the intersection point (i.e. at the y average value)
36574 /*Vector<double> vertex_coord2;
36575 unsigned n_intersect=0;
36576 double x_average=0.0;
36577
36578 for(unsigned p=0;p<n_polyline;p++)
36579 {
36580 //How many vertices are there in the segment
36581 unsigned n_vertex = poly_pt->polyline_pt(p)->nvertex();
36582 for(unsigned v=0;v<n_vertex-1;v++)
36583 {
36584 vertex_coord = poly_pt->polyline_pt(p)->vertex_coordinate(v);
36585 vertex_coord2 = poly_pt->polyline_pt(p)->vertex_coordinate(v+1);
36586 std::cout << vertex_coord[0] << " " << vertex_coord[1]
36587 << " " <<
36588 vertex_coord2[0] << " " <<
36589
36590 vertex_coord2[1] << "\n";
36591 //Does the line between vertices intersect the vertical position
36592 if((vertex_coord[1] -internal_point_coord[ihole][1])*
36593 (vertex_coord2[1] - internal_point_coord[ihole][1]) < 0.0)
36594 {
36595 ++n_intersect; x_average += 0.5*(vertex_coord[0] +
36596 vertex_coord2[0]);
36597 }
36598 }
36599 }
36600
36601 //Now just report the value if we have had intersections
36602 if(n_intersect != 0)
36603 {
36604 //Report
36605 std::cout << "I have computed a hole " << x_average << " " <<
36606 n_intersect << " "
36607 << x_average/((double)n_intersect) << std::endl;
36608 internal_point_coord[ihole][0] =
36609 x_average/((double)n_intersect);
36610 }
36611 */
36612
36613 // Set the new hole centre
36614 poly_pt->internal_point() = internal_point_coord[ihole];
36615 // std::cout << "I've had my centre updated to "
36616 // << internal_point_coord[ihole][0]
36617 // << " " << internal_point_coord[ihole][1] << "\n";
36618 }
36619 }
36620 }
36621 }
36622 } // End of the action (n_hole for)
36623
36624 if (check_only)
36625 {
36626 // If we make it up to here and we only check then no update is required
36627 return false;
36628 }
36629 else
36630 {
36631 // otherwise indicate whether an actual update was performed
36632 return update_was_performed;
36633 }
36634
36635 } // End of the loop of internal boundaries
36636
36637 //======================================================================
36638 /// Create the polylines and fill associate data structures, used when
36639 /// creating from a mesh from polyfiles
36640 //======================================================================
36641 template<class ELEMENT>
36643 const std::string& node_file_name, const std::string& poly_file_name)
36644 {
36645 // Get the nodes coordinates (the index of the nodes to build the
36646 // polylines is the one used in the node_file_name file)
36647 // Process node file
36648 // -----------------
36649 std::ifstream node_file(node_file_name.c_str(), std::ios_base::in);
36650
36651 // Check that the file actually opened correctly
36652 if (!node_file.is_open())
36653 {
36654 std::string error_msg("Failed to open node file: ");
36655 error_msg += "\"" + node_file_name + "\".";
36656 throw OomphLibError(
36657 error_msg, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36658 }
36659
36660 // Read number of nodes
36661 unsigned nnodes;
36662 node_file >> nnodes;
36663
36664 // Spatial dimension of nodes
36665 unsigned dimension;
36666 node_file >> dimension;
36667
36668#ifdef PARANOID
36669 if (dimension != 2)
36670 {
36671 throw OomphLibError("The dimension must be 2\n",
36672 OOMPH_CURRENT_FUNCTION,
36673 OOMPH_EXCEPTION_LOCATION);
36674 }
36675#endif
36676
36677 // Storage the nodes vertices
36678 Vector<double> x_node(nnodes);
36679 Vector<double> y_node(nnodes);
36680
36681 // Number of attributes
36682 unsigned npoint_attributes;
36683 node_file >> npoint_attributes;
36684 ;
36685
36686 // Flag for boundary markers
36687 unsigned boundary_markers_flag = 0;
36688 node_file >> boundary_markers_flag;
36689
36690 // Dummy for node number
36691 unsigned dummy_node_number;
36692 // Dummy for node attribute
36693 unsigned dummy_node_attribute;
36694 // Dummy for node boundary
36695 unsigned dummy_node_boundary;
36696
36697 // Load in nodal posititions, point attributes
36698 // and boundary markers
36699 for (unsigned i = 0; i < nnodes; i++)
36700 {
36701 node_file >> dummy_node_number;
36702 node_file >> x_node[i];
36703 node_file >> y_node[i];
36704 for (unsigned j = 0; j < npoint_attributes; ++j)
36705 {
36706 node_file >> dummy_node_attribute;
36707 }
36708 if (boundary_markers_flag)
36709 {
36710 node_file >> dummy_node_boundary;
36711 }
36712 }
36713 node_file.close();
36714
36715 // Get the segments information and use that info. to create the
36716 // polylines
36717
36718 // A map to store the segments associated to a boundary, non sorted
36719 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>
36720 unsorted_boundary_segments;
36721
36722 // Independent storage for the boundaries ids found in the segments so that
36723 // the polylines, and therefore polygons be created in the order they appear
36724 // in the polyfile
36725 Vector<unsigned> sorted_boundaries_ids;
36726
36727 // Process poly file to extract edges
36728 //-----------------------------------
36729
36730 // Open poly file
36731 std::ifstream poly_file(poly_file_name.c_str(), std::ios_base::in);
36732
36733 // Check that the file actually opened correctly
36734 if (!poly_file.is_open())
36735 {
36736 std::string error_msg("Failed to open poly file: ");
36737 error_msg += "\"" + poly_file_name + "\".";
36738 throw OomphLibError(
36739 error_msg, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36740 }
36741
36742 // Number of nodes in poly file --- these will be ignore
36743 unsigned n_node_poly;
36744 poly_file >> n_node_poly;
36745
36746 // Dimension
36747 poly_file >> dimension;
36748
36749 // Attribute flag
36750 unsigned attribute_flag;
36751 poly_file >> attribute_flag;
36752
36753 // Flag for boundary markers
36754 poly_file >> boundary_markers_flag;
36755
36756 // Ignore node information: Note: No, we can't extract the
36757 // actual nodes themselves from here!
36758 unsigned dummy;
36759 for (unsigned i = 0; i < n_node_poly; i++)
36760 {
36761 // Read in (and discard) node number and x and y coordinates
36762 poly_file >> dummy;
36763 poly_file >> dummy;
36764 poly_file >> dummy;
36765 // read in the attributes
36766 for (unsigned j = 0; j < attribute_flag; ++j)
36767 {
36768 poly_file >> dummy;
36769 }
36770 // read in the boundary marker
36771 if (boundary_markers_flag == 1)
36772 {
36773 poly_file >> dummy;
36774 }
36775 }
36776
36777 // Variable used to read the values from the input file
36778 unsigned read_value;
36779
36780 // Number of segments
36781 poly_file >> read_value;
36782 const unsigned nglobal_segments = read_value;
36783
36784 // Boundary marker flag
36785 poly_file >> boundary_markers_flag;
36786
36787 // Global segment number
36788 unsigned global_segment_number;
36789
36790 // Node identifier set (used to identify possible internal boundaries)
36791 std::set<unsigned> nodes_ids;
36792
36793 // Extract information for each segment
36794 for (unsigned i = 0; i < nglobal_segments; i++)
36795 {
36796 // Node id on the edge of the segment
36797 unsigned lnode_id = 0; // left node
36798 unsigned rnode_id = 0; // right node
36799 unsigned bnd_id = 0; // boundary id associated to the current segment
36800 poly_file >> global_segment_number;
36801 poly_file >> lnode_id;
36802 poly_file >> rnode_id;
36803 nodes_ids.insert(lnode_id);
36804 nodes_ids.insert(rnode_id);
36805 if (boundary_markers_flag)
36806 {
36807 poly_file >> bnd_id;
36808 }
36809
36810 // Store the segments info. (use bnd_id - 1 because the nodes and
36811 // elements associated the bnd_id have been associated by external
36812 // methods to bnd_id - 1)
36813 unsorted_boundary_segments[bnd_id - 1].push_back(
36814 std::make_pair(lnode_id, rnode_id));
36815
36816 // Add the boundary id to the vector of boundaries ids only if it
36817 // has not been added, the polylines will be created using this
36818 // order
36819
36820 // Get the number of boundaries ids currently sorted
36821 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
36822 // Flag to know if the boundary id was found
36823 bool boundary_id_found = false;
36824 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
36825 {
36826 if (sorted_boundaries_ids[ib] == bnd_id - 1)
36827 {
36828 boundary_id_found = true;
36829 break;
36830 } // if (sorted_boundaries_ids[ib] == bnd_id - 1)
36831 } // for (ib < nsorted_boundaries_ids)
36832
36833 // If th boundary id has not been added, then add it!!!
36834 if (!boundary_id_found)
36835 {
36836 sorted_boundaries_ids.push_back(bnd_id - 1);
36837 } // if (!boundary_id_found)
36838 }
36839
36840 // Verify if there are internal boundaries defined, if that is the
36841 // case we can not continue since we are not yet supporting internal
36842 // boundaries defined in polyfiles to created a mesh that may be
36843 // adapted
36844#ifdef PARANOID
36845 if (nglobal_segments != nodes_ids.size())
36846 {
36847 std::ostringstream error_message;
36848 error_message
36849 << "The number of nodes (" << nodes_ids.size() << ") and segments ("
36850 << nglobal_segments << ") is different.\nThis may mean that there "
36851 << "are internal non-closed boundaries defined in\nthe polyfile. "
36852 << "If you need this feature please use the TriangleMeshPoyLine\n"
36853 << "and TriangleMeshCurviLine objects to define your domain.\n\n";
36854 throw OomphLibError(
36855 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36856 }
36857#endif
36858
36859 // Now sort the segments associated to a boundary to create a contiguous
36860 // polyline, but first check that the number of found boundaries be the
36861 // same as the current number of boundaries in the mesh
36862 const unsigned nboundary = unsorted_boundary_segments.size();
36863
36864#ifdef PARANOID
36865 if (nboundary != this->nboundary())
36866 {
36867 std::ostringstream error_message;
36868 error_message
36869 << "The number of boundaries on the mesh (" << this->nboundary()
36870 << ") is different from the number of\nboundaries read from the "
36871 << "polyfiles (" << unsorted_boundary_segments.size() << ")!!!\n\n\n";
36872 throw OomphLibError(
36873 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36874 }
36875#endif
36876
36877 // Get the number of sorted boundaries ids and check that it matches
36878 // with the total number of boundaries
36879 const unsigned nsorted_boundaries_ids = sorted_boundaries_ids.size();
36880#ifdef PARANOID
36881 if (nsorted_boundaries_ids != this->nboundary())
36882 {
36883 std::ostringstream error_message;
36884 error_message
36885 << "The number of boundaries on the mesh (" << this->nboundary()
36886 << ") is different from the number of\nsorted boundaries ids read "
36887 << "from the polyfiles (" << nsorted_boundaries_ids << ")!!!\n\n\n";
36888 throw OomphLibError(
36889 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
36890 }
36891#endif
36892
36893 // Sorted segments (to create a polyline -- boundary)
36894 std::map<unsigned, std::list<unsigned>> sorted_boundary_segments;
36895
36896 // Go through all the found boundaries
36897 std::map<unsigned, Vector<std::pair<unsigned, unsigned>>>::iterator it;
36898
36899 for (it = unsorted_boundary_segments.begin();
36900 it != unsorted_boundary_segments.end();
36901 it++)
36902 {
36903 // Get the current boundary id, only look for the segments
36904 // associated with this boundary
36905 const unsigned bnd_id = (*it).first;
36906 Vector<std::pair<unsigned, unsigned>> segments_edges = (*it).second;
36907
36908 // Now sort the segments associated to this boundary
36909 std::map<std::pair<unsigned, unsigned>, bool> segment_done;
36910 const unsigned nsegments = segments_edges.size();
36911
36912 // Sorted nodes for the current segment
36913 std::list<unsigned> sorted_segments;
36914
36915 // Get the left and right node of the zero segment
36916 unsigned left_node_id = segments_edges[0].first;
36917 unsigned right_node_id = segments_edges[0].second;
36918
36919 // ... and add it to the sorted segments structure
36920 sorted_segments.push_back(left_node_id);
36921 sorted_segments.push_back(right_node_id);
36922
36923 // Mark the current segment as done
36924 segment_done[segments_edges[0]] = true;
36925
36926 // Set the number of sorted segments
36927 unsigned nsorted_segments = 1;
36928
36929 while (nsorted_segments < nsegments)
36930 {
36931 for (unsigned i = 1; i < nsegments; i++)
36932 {
36933 // Check if the i-th segments has been done
36934 if (!segment_done[segments_edges[i]])
36935 {
36936 // Get the left and right node id
36937 unsigned current_left_node_id = segments_edges[i].first;
36938 unsigned current_right_node_id = segments_edges[i].second;
36939
36940 // Now check if the current segment can be added to the left
36941 // or right side of the sorted segments
36942 if (current_left_node_id == right_node_id)
36943 {
36944 // Add the current_right_node_id to the right of the sorted
36945 // segments
36946 sorted_segments.push_back(current_right_node_id);
36947 // Increase the number of sorted segments
36948 nsorted_segments++;
36949 // Mark the segment as done
36950 segment_done[segments_edges[i]] = true;
36951 // Update the right most node
36952 right_node_id = current_right_node_id;
36953 // Break the for loop
36954 break;
36955 }
36956 else if (current_right_node_id == left_node_id)
36957 {
36958 // Add the current_left_node_id to the left of the sorted
36959 // segments
36960 sorted_segments.push_front(current_left_node_id);
36961 // Increase the number of sorted segments
36962 nsorted_segments++;
36963 // Mark the segment as done
36964 segment_done[segments_edges[i]] = true;
36965 // Update the left most node
36966 left_node_id = current_left_node_id;
36967 // Break the for loop
36968 break;
36969 }
36970 else if (current_left_node_id == left_node_id)
36971 {
36972 // Add the current_right_node_id to the left of the sorted
36973 // segments
36974 sorted_segments.push_front(current_right_node_id);
36975 // Increase the number of sorted segments
36976 nsorted_segments++;
36977 // Mark the segment as done
36978 segment_done[segments_edges[i]] = true;
36979 // Update the left most node
36980 left_node_id = current_right_node_id;
36981 // Break the for loop
36982 break;
36983 }
36984 else if (current_right_node_id == right_node_id)
36985 {
36986 // Add the current_left_node_id to the right of the sorted
36987 // segments
36988 sorted_segments.push_back(current_left_node_id);
36989 // Increase the number of sorted segments
36990 nsorted_segments++;
36991 // Mark the segment as done
36992 segment_done[segments_edges[i]] = true;
36993 // Update the left most node
36994 right_node_id = current_left_node_id;
36995 // Break the for loop
36996 break;
36997 }
36998 } // if (!segment_done[segments_edges[i]])
36999 } // for (i < nsegments)
37000 } // while(nsorted_segments < nsegments)
37001
37002 sorted_boundary_segments[bnd_id] = sorted_segments;
37003
37004 } // for (unsorted_boundary_segments.begin();
37005 // unsorted_boundary_segments.end())
37006
37007#ifdef PARANOID
37008 if (sorted_boundary_segments.size() != this->nboundary())
37009 {
37010 std::ostringstream error_message;
37011 error_message
37012 << "The number of boundaries on the mesh (" << this->nboundary()
37013 << ") is different from the number\nof sorted boundaries to create the "
37014 << "polylines (" << sorted_boundary_segments.size() << ")\n\n";
37015 throw OomphLibError(
37016 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37017 }
37018#endif
37019
37020 // Now we have the sorted nodes, we can create the polylines by
37021 // getting the vertices of the nodes
37022 Vector<TriangleMeshPolyLine*> polylines_pt(nboundary);
37023 unsigned current_polyline = 0;
37024
37025 // Go through the sorted boundaries using the sorted boundaries ids
37026 for (unsigned ib = 0; ib < nsorted_boundaries_ids; ib++)
37027 {
37028 // Get the boundary id from the vector of sorted boundaries ids
37029 const unsigned bnd_id = sorted_boundaries_ids[ib];
37030
37031 // Create a vector representation for ease to use
37032 // Get the vertices of the nodes that create the boundary / polyline
37033 Vector<unsigned> nodes_ids;
37034 for (std::list<unsigned>::iterator it_list =
37035 sorted_boundary_segments[bnd_id].begin();
37036 it_list != sorted_boundary_segments[bnd_id].end();
37037 it_list++)
37038 {
37039 nodes_ids.push_back((*it_list));
37040 }
37041
37042 // Get the number of vertices for the polyline
37043 const unsigned nvertices = nodes_ids.size();
37044
37045 // The storage for the vertices
37046 Vector<Vector<double>> vertices(nvertices);
37047
37048 // Now get the vertices of the nodes of the current boundary
37049 for (unsigned i = 0; i < nvertices; i++)
37050 {
37051 // Get the vertices
37052 vertices[i].resize(2);
37053 vertices[i][0] = x_node[nodes_ids[i] - 1];
37054 vertices[i][1] = y_node[nodes_ids[i] - 1];
37055 }
37056
37057 // Now create the polyline
37058
37059 // Note: The bnd_id is the real bnd_id (from the input file) - 1
37060 // since nodes and elements of the current boundary have been
37061 // associated to bnd_id - 1)
37062 polylines_pt[current_polyline] =
37063 new TriangleMeshPolyLine(vertices, bnd_id);
37064
37065 // Updates bnd_id<--->curve section map
37066 this->Boundary_curve_section_pt[bnd_id] =
37067 dynamic_cast<TriangleMeshCurveSection*>(polylines_pt[current_polyline]);
37068
37069 // Increase the index for the polyline storage
37070 current_polyline++;
37071
37072 } // for (it_sorted = sorted_boundary_segments.begin();
37073 // it_sorted != sorted_boundary_segments.end())
37074
37075 // Now create the polygons or closed curves
37076 // Sort the polylines to create polygons
37077 unsigned nsorted_polylines = 0;
37078
37079 // Number of created polygons
37080 unsigned npolygons = 0;
37081
37082 // Storage for the polygons
37083 Vector<TriangleMeshPolygon*> polygons_pt;
37084
37085 // Mark the already done polylines
37086 std::map<unsigned, bool> polyline_done;
37087 while (nsorted_polylines < nboundary)
37088 {
37089 // Storage for the curve sections that create a polygon
37090 std::list<TriangleMeshCurveSection*> sorted_curve_sections_pt;
37091
37092 unsigned init_poly = 0;
37093#ifdef PARANOID
37094 bool found_root_polyline = false;
37095#endif
37096 // Get the left and right node of the current polyline
37097 for (unsigned i = 0; i < nboundary; i++)
37098 {
37099 if (!polyline_done[i])
37100 {
37101 init_poly = i;
37102 // Increase the number of sorted polylines
37103 nsorted_polylines++;
37104#ifdef PARANOID
37105 // Mark as found the root polyline
37106 found_root_polyline = true;
37107#endif
37108 // Mark the polyline as done
37109 polyline_done[i] = true;
37110 // Add the polyline to the curve sections storage
37111 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37112 // Break the loop to set we have found a root polyline
37113 break;
37114 }
37115 }
37116
37117#ifdef PARANOID
37118 if (!found_root_polyline)
37119 {
37120 std::ostringstream error_message;
37121 error_message << "Was not possible to found the root polyline to "
37122 "create polygons\n\n";
37123 throw OomphLibError(error_message.str(),
37124 OOMPH_CURRENT_FUNCTION,
37125 OOMPH_EXCEPTION_LOCATION);
37126 }
37127#endif
37128
37129 // Get the associated boundary to the current polyline
37130 const unsigned bnd_id = polylines_pt[init_poly]->boundary_id();
37131 // Get the initial and final node id of the current polyline
37132 unsigned left_node_id = sorted_boundary_segments[bnd_id].front();
37133 unsigned right_node_id = sorted_boundary_segments[bnd_id].back();
37134
37135 // Flag to know that we already have a closed polygon
37136 bool closed_polygon = false;
37137
37138 do
37139 {
37140 // Go through all the polylines
37141 for (unsigned i = init_poly; i < nboundary; i++)
37142 {
37143 // Check that the polyline has not been currently done
37144 if (!polyline_done[i])
37145 {
37146 // Get the initial and final nodes id of the current polyline
37147
37148 // Get the associated boundary to the current polyline
37149 const unsigned cbnd_id = polylines_pt[i]->boundary_id();
37150 // Get the initial and final node id of the current polyline
37151 unsigned cleft_node_id = sorted_boundary_segments[cbnd_id].front();
37152 unsigned cright_node_id = sorted_boundary_segments[cbnd_id].back();
37153
37154 // Check if the polyline goes to the left or right of the
37155 // current sorted polylines
37156 if (cleft_node_id == right_node_id)
37157 {
37158 // Add the polyline to the curve section storage
37159 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37160 // Mark the polyline as done
37161 polyline_done[i] = true;
37162 // Update the right node
37163 right_node_id = cright_node_id;
37164 // Increase the number of done polyines
37165 nsorted_polylines++;
37166 // Break the for loop
37167 break;
37168 }
37169 else if (cright_node_id == left_node_id)
37170 {
37171 // Add the polyline to the curve section storage
37172 sorted_curve_sections_pt.push_front(polylines_pt[i]);
37173 // Mark the polyline as done
37174 polyline_done[i] = true;
37175 // Update the right node
37176 left_node_id = cleft_node_id;
37177 // Increase the number of done polyines
37178 nsorted_polylines++;
37179 // Break the for loop
37180 break;
37181 }
37182 else if (cleft_node_id == left_node_id)
37183 {
37184 // First reverse the polyline
37185 polylines_pt[i]->reverse();
37186 // Add the polyline to the curve section storage
37187 sorted_curve_sections_pt.push_front(polylines_pt[i]);
37188 // Mark the polyline as done
37189 polyline_done[i] = true;
37190 // Update the right node
37191 left_node_id = cright_node_id;
37192 // Increase the number of done polyines
37193 nsorted_polylines++;
37194 // Break the for loop
37195 break;
37196 }
37197 else if (cright_node_id == right_node_id)
37198 {
37199 // First reverse the polyline
37200 polylines_pt[i]->reverse();
37201 // Add the polyline to the curve section storage
37202 sorted_curve_sections_pt.push_back(polylines_pt[i]);
37203 // Mark the polyline as done
37204 polyline_done[i] = true;
37205 // Update the right node
37206 right_node_id = cleft_node_id;
37207 // Increase the number of done polyines
37208 nsorted_polylines++;
37209 // Break the for loop
37210 break;
37211 }
37212 } // if (!polyline_done[i])
37213
37214 } // for (i < nboundary)
37215
37216 // We have created a polygon
37217 if (left_node_id == right_node_id)
37218 {
37219 // Set the flag as true
37220 closed_polygon = true;
37221 }
37222
37223 } while (nsorted_polylines < nboundary && !closed_polygon);
37224
37225#ifdef PARANOID
37226 if (!closed_polygon)
37227 {
37228 std::ostringstream error_message;
37229 error_message
37230 << "It was not possible to create a closed curve, these are the "
37231 << "vertices of the already sorted polylines\n\n";
37232 unsigned cpolyline = 0;
37233 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37234 sorted_curve_sections_pt.begin();
37235 it_list != sorted_curve_sections_pt.end();
37236 it_list++)
37237 {
37238 error_message << "Polyline (" << cpolyline << ")\n";
37239 TriangleMeshPolyLine* tmp_poly_pt =
37240 dynamic_cast<TriangleMeshPolyLine*>((*it_list));
37241 const unsigned nvertex = tmp_poly_pt->nvertex();
37242 for (unsigned v = 0; v < nvertex; v++)
37243 {
37244 error_message << "(" << tmp_poly_pt->vertex_coordinate(v)[0] << ", "
37245 << tmp_poly_pt->vertex_coordinate(v)[1] << ")\n";
37246 }
37247 error_message << "\n";
37248 cpolyline++;
37249 }
37250 throw OomphLibError(error_message.str(),
37251 OOMPH_CURRENT_FUNCTION,
37252 OOMPH_EXCEPTION_LOCATION);
37253 }
37254#endif
37255
37256 // Create a vector version to create the polygon from the sorted
37257 // polyines
37258 Vector<TriangleMeshCurveSection*> tmp_sorted_curve_sections_pt;
37259 for (std::list<TriangleMeshCurveSection*>::iterator it_list =
37260 sorted_curve_sections_pt.begin();
37261 it_list != sorted_curve_sections_pt.end();
37262 it_list++)
37263 {
37264 tmp_sorted_curve_sections_pt.push_back((*it_list));
37265 }
37266
37267 // Create a new polygon by using the new created polylines
37268 TriangleMeshPolygon* polygon_pt =
37269 new TriangleMeshPolygon(tmp_sorted_curve_sections_pt);
37270
37271 // Keep track of new created polygons that need to be deleted!!!
37272 this->Free_polygon_pt.insert(polygon_pt);
37273
37274 // Store the polygon in the polygons storages
37275 polygons_pt.push_back(polygon_pt);
37276
37277 npolygons++;
37278
37279 } // while(nsorted_polylines < nboundary)
37280
37281 // ------------------------------------------------------------------
37282 // Before filling the data structures we need to identify the outer
37283 // closed boundary and the inner closed boundaries.
37284 // If the nodes are not in order we throw a warning message
37285
37286 // Index for the polygon that is currently considered as the outer
37287 // boundary
37288 unsigned index_outer = 0;
37289
37290 for (unsigned idx_outer = 0; idx_outer < npolygons; idx_outer++)
37291 {
37292 // Get the vertices of the outer boundary
37293 Vector<Vector<double>> outer_vertex_coordinates;
37294
37295 // Flag to know if ALL the inner closed boundaries are inside the
37296 // outer closed boundary
37297 bool all_inner_inside = true;
37298
37299 // Number of polylines of the outer boundary
37300 const unsigned nouter_polylines = polygons_pt[idx_outer]->npolyline();
37301 for (unsigned p = 0; p < nouter_polylines; p++)
37302 {
37303 TriangleMeshPolyLine* tmp_poly_pt =
37304 polygons_pt[idx_outer]->polyline_pt(p);
37305 const unsigned nvertex = tmp_poly_pt->nvertex();
37306 for (unsigned v = 0; v < nvertex; v++)
37307 {
37308 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37309 outer_vertex_coordinates.push_back(current_vertex);
37310 } // for (v < nvertex)
37311 } // for (p < nouter_polylines)
37312
37313 // Now get the vertices for the inner boundaries
37314
37315 // First get the number of inner closed boundaries (polygons size
37316 // minus one because one of the polygons is considered to be the
37317 // outer closed boundary
37318 const unsigned ninner_polygons = polygons_pt.size() - 1;
37319
37320 // Store the vertices of the inner closed boundaries
37321 Vector<Vector<Vector<double>>> inner_vertex_coordinates(ninner_polygons);
37322 // Get all the vertices of the inner closed boundaries
37323 for (unsigned i = 0; i <= ninner_polygons; i++)
37324 {
37325 if (i != idx_outer)
37326 {
37327 // Number of polylines of the current internal closed boundary
37328 const unsigned ninner_polylines = polygons_pt[i]->npolyline();
37329 for (unsigned p = 0; p < ninner_polylines; p++)
37330 {
37331 TriangleMeshPolyLine* tmp_poly_pt = polygons_pt[i]->polyline_pt(p);
37332 const unsigned nvertex = tmp_poly_pt->nvertex();
37333 for (unsigned v = 0; v < nvertex; v++)
37334 {
37335 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37336 if (i < idx_outer)
37337 {
37338 inner_vertex_coordinates[i].push_back(current_vertex);
37339 }
37340 else if (i > idx_outer)
37341 {
37342 inner_vertex_coordinates[i - 1].push_back(current_vertex);
37343 }
37344 } // for (v < nvertex)
37345
37346 } // for (p < ninner_polylines)
37347
37348 } // if (i != index_outer)
37349
37350 } // for (i <= ninner_polygons)
37351
37352 // Now check that ALL the vertices of ALL the internal closed
37353 // boundaries are inside the outer closed boundary
37354 for (unsigned i = 0; i < ninner_polygons; i++)
37355 {
37356 // Get the number of vertices in the current internal closed
37357 // boundary
37358 const unsigned nvertex_internal = inner_vertex_coordinates[i].size();
37359 for (unsigned v = 0; v < nvertex_internal; v++)
37360 {
37361 // Get a vertex in the current internal closed boundary
37362 Vector<double> current_point = inner_vertex_coordinates[i][v];
37363 all_inner_inside &= this->is_point_inside_polygon_helper(
37364 outer_vertex_coordinates, current_point);
37365
37366 // Check if we should continue checking for more points inside
37367 // the current proposed outer boundary
37368 if (!all_inner_inside)
37369 {
37370 // Break the "for" for the vertices
37371 break;
37372 }
37373
37374 } // for (v < nvertex_internal)
37375
37376 // Check if we should continue checking for more inner closed
37377 // boundaries inside the current proposed outer boundary
37378 if (!all_inner_inside)
37379 {
37380 // Break the "for" for the inner boundaries
37381 break;
37382 }
37383
37384 } // for (i < ninner_polygons)
37385
37386 // Check if all the vertices of all the polygones are inside the
37387 // current proposed outer boundary
37388 if (all_inner_inside)
37389 {
37390 index_outer = idx_outer;
37391 break;
37392 }
37393
37394 } // for (idx_outer < npolygons)
37395
37396#ifdef PARANOID
37397 // Check if the first nodes listed in the polyfiles correspond to
37398 // the outer boundary, if that is not the case then throw a warning
37399 // message
37400 if (index_outer != 0)
37401 {
37402 std::ostringstream warning_message;
37403 warning_message
37404 << "The first set of nodes listed in the input polyfiles does not\n"
37405 << "correspond to the outer closed boundary. This may lead to\n"
37406 << "problems at the adaptation stage if the holes coordinates\n"
37407 << "are no correctly associated to the inner closed boundaries.\n"
37408 << "You can check the generated mesh by calling the output() method\n"
37409 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37410 OomphLibWarning(warning_message.str(),
37411 OOMPH_CURRENT_FUNCTION,
37412 OOMPH_EXCEPTION_LOCATION);
37413 } // if (index_outer != 0)
37414#endif
37415
37416 // ------------------------------------------------------------------
37417 // Now fill the data structures
37418
37419 // Store outer polygon
37420 // We are assuming there is only one outer polygon
37421 this->Outer_boundary_pt.resize(1);
37422 this->Outer_boundary_pt[0] = polygons_pt[index_outer];
37423
37424 this->Internal_polygon_pt.resize(npolygons - 1);
37425 for (unsigned i = 0; i < npolygons; i++)
37426 {
37427 if (i != index_outer)
37428 {
37429 if (i < index_outer)
37430 {
37431 // Store internal polygons by copy constructor
37432 this->Internal_polygon_pt[i] = polygons_pt[i];
37433 }
37434 else if (i > index_outer)
37435 {
37436 // Store internal polygons by copy constructor
37437 this->Internal_polygon_pt[i - 1] = polygons_pt[i];
37438 }
37439 } // if (i != index_outer)
37440 } // for (i < npolygons)
37441
37442 // Before assigning the hole vertex coordinate to the inner closed
37443 // boundaries check that the holes are listed in orderm if that is
37444 // not the case the associate each hole vertex coordinate to the
37445 // inner closed boundaries
37446
37447 // Store the vertices of the inner closed boundaries
37448 Vector<Vector<Vector<double>>> inner_vertex_coordinates(npolygons - 1);
37449 // Get all the vertices of the inner closed boundaries
37450 for (unsigned i = 0; i < npolygons - 1; i++)
37451 {
37452 // Number of polylines of the current internal closed boundary
37453 const unsigned ninner_polylines =
37454 this->Internal_polygon_pt[i]->npolyline();
37455 for (unsigned p = 0; p < ninner_polylines; p++)
37456 {
37457 TriangleMeshPolyLine* tmp_poly_pt =
37458 this->Internal_polygon_pt[i]->polyline_pt(p);
37459 // Number of vertices of the current polyline in the current
37460 // internal closed polygon
37461 const unsigned nvertex = tmp_poly_pt->nvertex();
37462 for (unsigned v = 0; v < nvertex; v++)
37463 {
37464 Vector<double> current_vertex = tmp_poly_pt->vertex_coordinate(v);
37465 inner_vertex_coordinates[i].push_back(current_vertex);
37466 } // for (v < nvertex)
37467
37468 } // for (p < ninner_polylines)
37469
37470 } // for (i <= ninner_polygons)
37471
37472 // Holes information
37473 unsigned nholes;
37474 poly_file >> nholes;
37475
37476#ifdef PARANOID
37477 if (npolygons > 1 && (npolygons - 1) != nholes)
37478 {
37479 std::ostringstream error_message;
37480 error_message
37481 << "The number of holes (" << nholes << ") does not correspond "
37482 << "with the number\nof internal polygons (" << npolygons - 1 << ")\n\n"
37483 << "Using polyfiles as input does not currently allows the\n"
37484 << "definition of more than one outer polygon\n\n";
37485 throw OomphLibError(
37486 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37487 }
37488#endif
37489
37490 // Storage for the holes
37491 Vector<Vector<double>> hole_coordinates(nholes);
37492
37493 // Dummy for hole number
37494 unsigned dummy_hole;
37495 // Loop over the holes to get centre coords
37496 for (unsigned ihole = 0; ihole < nholes; ihole++)
37497 {
37498 hole_coordinates[ihole].resize(2);
37499 // Read the centre value
37500 poly_file >> dummy_hole;
37501 poly_file >> hole_coordinates[ihole][0];
37502 poly_file >> hole_coordinates[ihole][1];
37503 }
37504
37505 // Vector that store the index of the hole coordinate that
37506 // correspond to each internal closed polygon
37507 Vector<unsigned> index_hole_of_internal_polygon(npolygons - 1);
37508 std::map<unsigned, bool> hole_done;
37509
37510 // Now associate each hole vertex to a corresponding internal closed
37511 // polygon
37512 for (unsigned i = 0; i < npolygons - 1; i++)
37513 {
37514 // Find which hole is associated to each internal closed boundary
37515 for (unsigned h = 0; h < nholes; h++)
37516 {
37517 // If the hole has not been previously associated
37518 if (!hole_done[h])
37519 {
37520 // Get the hole coordinate
37521 Vector<double> current_point = hole_coordinates[h];
37522
37523 const bool hole_in_polygon = this->is_point_inside_polygon_helper(
37524 inner_vertex_coordinates[i], current_point);
37525
37526 // If the hole is inside the polygon
37527 if (hole_in_polygon)
37528 {
37529 // Mark the hole as done
37530 hole_done[h] = true;
37531 // Associate the current hole with the current inner closed
37532 // boundary
37533 index_hole_of_internal_polygon[i] = h;
37534 // Break the search
37535 break;
37536 }
37537
37538 } // if (!hole_done[h])
37539
37540 } // for (h < nholes)
37541
37542 } // for (i < npolygons-1)
37543
37544#ifdef PARANOID
37545 if (hole_done.size() != npolygons - 1)
37546 {
37547 std::ostringstream error_message;
37548 error_message
37549 << "Not all the holes were associated to an internal closed boundary\n"
37550 << "Only (" << hole_done.size()
37551 << ") holes were assigned for a total of\n"
37552 << "(" << npolygons - 1 << ") internal closed boundaries.\n"
37553 << "You can check the generated mesh by calling the output() method\n"
37554 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37555 throw OomphLibError(
37556 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
37557 } // if (index_hole != ihole)
37558#endif
37559
37560 // Assign the holes coordinates to the internal polygons
37561 for (unsigned ihole = 0; ihole < nholes; ihole++)
37562 {
37563 // Get the index hole of the current internal closed polygon
37564 const unsigned index_hole = index_hole_of_internal_polygon[ihole];
37565#ifdef PARANOID
37566 // Check if the hole index is the same as the internal closed
37567 // boundary, it means that the holes were listed in the same order
37568 // as the nodes of the internal closed boundaries
37569 if (index_hole != ihole)
37570 {
37571 std::ostringstream error_message;
37572 error_message
37573 << "The hole vertices coordinates are not listed in the same order\n"
37574 << "as the nodes that define the internal closed boundaries.\n"
37575 << "This may lead to problems in case that the holes coordinates\n"
37576 << "were no properly assigned to the internal closed boundaries.\n"
37577 << "You can check the generated mesh by calling the output() method\n"
37578 << "from the mesh object '(problem.mesh_pt()->output(string))'\n\n";
37579 throw OomphLibError(error_message.str(),
37580 OOMPH_CURRENT_FUNCTION,
37581 OOMPH_EXCEPTION_LOCATION);
37582 } // if (index_hole != ihole)
37583#endif
37584
37585 // Set the hole coordinate for the internal polygon
37586 this->Internal_polygon_pt[ihole]->internal_point() =
37587 hole_coordinates[index_hole];
37588 }
37589
37590 // Ignore the first line with structure description
37591 poly_file.ignore(80, '\n');
37592
37593 // Regions information
37594 unsigned nregions;
37595
37596 // Extract regions information
37597 // But first check if there are regions or not
37598 std::string regions_info_string;
37599
37600 // Read line up to termination sign
37601 getline(poly_file, regions_info_string);
37602
37603 // Check if the read string is a number or a comment wrote by triangle,
37604 // if it is a number then that is the number of regions
37605 if (isdigit(regions_info_string.c_str()[0]))
37606 {
37607 nregions = std::atoi(regions_info_string.c_str());
37608 }
37609 else
37610 {
37611 nregions = 0;
37612 }
37613
37614 // The regions coordinates
37615 std::map<unsigned, Vector<double>> regions_coordinates;
37616
37617 // Dummy for regions number
37618 unsigned dummy_region;
37619
37620 unsigned region_id;
37621
37622 // Loop over the regions to get their coords
37623 for (unsigned iregion = 0; iregion < nregions; iregion++)
37624 {
37625 Vector<double> tmp_region_coordinates(2);
37626 // Read the regions coordinates
37627 poly_file >> dummy_region;
37628 poly_file >> tmp_region_coordinates[0];
37629 poly_file >> tmp_region_coordinates[1];
37630 poly_file >> region_id;
37631 regions_coordinates[region_id].resize(2);
37632 regions_coordinates[region_id][0] = tmp_region_coordinates[0];
37633 regions_coordinates[region_id][1] = tmp_region_coordinates[1];
37634
37635 // Ignore the first line with structure description
37636 poly_file.ignore(80, '\n');
37637
37638 // Verify if not using the default region number (zero)
37639 if (region_id == 0)
37640 {
37641 std::ostringstream error_message;
37642 error_message
37643 << "Please use another region id different from zero.\n"
37644 << "It is internally used as the default region number.\n";
37645 throw OomphLibError(error_message.str(),
37646 OOMPH_CURRENT_FUNCTION,
37647 OOMPH_EXCEPTION_LOCATION);
37648 }
37649 }
37650
37651 // Store the extra regions coordinates
37652 this->Regions_coordinates = regions_coordinates;
37653
37654 poly_file.close();
37655 }
37656
37657 //======================================================================
37658 /// Updates the polygon but using the elements area instead of
37659 /// the default refinement and unrefinement methods
37660 //======================================================================
37661 template<class ELEMENT>
37663 TriangleMeshPolygon*& polygon_pt, const Vector<double>& target_area)
37664 {
37665 // Verify that there was a change on the polygon representation
37666 unsigned update_was_performed = false;
37667
37668 const unsigned nele = this->nelement();
37669
37670 // - Get the vertices along the boundaries and for each element identify
37671 // its associated target error.
37672 // - Get face mesh representation of each polyline.
37673 // - Get the vertices with the help of face elements.
37674 // - Find the global index in the mesh of the face element and use
37675 // it to get its associated target area
37676
37677 // Get the face mesh representation
37678 Vector<Mesh*> face_mesh_pt;
37679 get_face_mesh_representation(polygon_pt, face_mesh_pt);
37680
37681 // Create vertices of the polylines by using the vertices of the
37682 // FaceElements
37683 Vector<double> vertex_coord(3); // zeta,x,y
37684 Vector<double> bound_left(1);
37685 Vector<double> bound_right(1);
37686
37687 unsigned n_polyline = polygon_pt->npolyline();
37688
37689 // Go for each polyline
37690 for (unsigned p = 0; p < n_polyline; p++)
37691 {
37692 // Get the MeshAsGeomObject representation just once per polyline,
37693 // this object is only used by the
37694 // refine_boundary_constrained_by_target_area() method. We get it
37695 // here to ensure that all processors (in a distributed context)
37696 // get this representation just once, and because an AllToAll MPI
37697 // communication is used in this calling
37698 MeshAsGeomObject* mesh_geom_obj_pt =
37699 new MeshAsGeomObject(face_mesh_pt[p]);
37700
37701 // Set of coordinates on the boundary
37702 // Set entries are ordered on first entry in vector which stores
37703 // the boundary coordinate so the vertices come out in order!
37704 std::set<Vector<double>> vertex_nodes;
37705
37706 // Vector to store the vertices, transfer the sorted vertices from the
37707 // set to this vector, --- including the z-value ---
37708 Vector<Vector<double>> tmp_vector_vertex_node;
37709
37710 // Vector to store the coordinates of the polylines, same as the
37711 // tmp_vector_vertex_node vector (after adding more nodes) but
37712 // --- without the z-value ---, used to re-generate the polylines
37713 Vector<Vector<double>> vector_vertex_node;
37714
37715#ifdef OOMPH_HAS_MPI
37716 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
37717 // Set of coordinates that are on the boundary (splitted boundary version)
37718 // The first vector is used to allocate the points for each sub-boundary
37719 // Set entries are ordered on first entry in vector which stores
37720 // the boundary coordinate so the vertices come out in order!
37721 Vector<std::set<Vector<double>>> sub_vertex_nodes;
37722
37723 // Vector to store the vertices, transfer the sorted vertices from the
37724 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
37725 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
37726
37727 // Vector to store the coordinates of the polylines that will represent
37728 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
37729 // but --- without the z-value ---, used to generate the sub-polylines
37730 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
37731 // --------- Stuff to deal with splitted boundaries ----------- End ------
37732#endif
37733
37734 // Get the boundary id
37735 const unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
37736
37737 // Get the chunk number
37738 const unsigned chunk = polygon_pt->curve_section_pt(p)->boundary_chunk();
37739
37740 /// Use a vector of vector for vertices and target areas to deal
37741 /// with the cases when the boundaries are split by the
37742 /// distribution process
37743
37744 // Loop over the face elements (ordered) and add their vertices
37745 const unsigned nface_element = face_mesh_pt[p]->nelement();
37746
37747 // Store the non halo face elements, the ones from which we will
37748 // get the vertices
37749 Vector<FiniteElement*> non_halo_face_element_pt;
37750
37751 // Map to store the index of the face element on a boundary
37752 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
37753
37754 for (unsigned ef = 0; ef < nface_element; ++ef)
37755 {
37756 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
37757#ifdef OOMPH_HAS_MPI
37758 // Skip the halo elements if working with a distributed mesh
37759 if (this->is_mesh_distributed() && ele_face_pt->is_halo())
37760 {
37761 continue;
37762 }
37763#endif
37764 // Add the face element to the vector
37765 non_halo_face_element_pt.push_back(ele_face_pt);
37766 face_element_index_on_boundary[ele_face_pt] = ef;
37767 }
37768
37769 // Get the number of non halo face element
37770 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
37771
37772 // Map to know the already sorted face elements
37773 std::map<FiniteElement*, bool> face_element_done;
37774
37775 // Number of done face elements
37776 unsigned nsorted_face_elements = 0;
37777
37778#ifdef OOMPH_HAS_MPI
37779 // Counter for sub_boundaries
37780 unsigned nsub_boundaries = 0;
37781#endif // #ifdef OOMPH_HAS_MPI
37782
37783 // Continue until all the face elements have been sorted
37784 // While to deal with split boundaries cases
37785 while (nsorted_face_elements < nnon_halo_face_element)
37786 {
37787 // Get and initial face element
37788 FiniteElement* ele_face_pt = 0;
37789#ifdef PARANOID
37790 bool found_initial_face_element = false;
37791#endif
37792
37793 unsigned iface = 0;
37794 for (iface = 0; iface < nnon_halo_face_element; iface++)
37795 {
37796 ele_face_pt = non_halo_face_element_pt[iface];
37797 // If not done then take it as initial face element
37798 if (!face_element_done[ele_face_pt])
37799 {
37800#ifdef PARANOID
37801 found_initial_face_element = true;
37802#endif
37803 nsorted_face_elements++;
37804 iface++;
37805 break;
37806 }
37807 }
37808
37809#ifdef PARANOID
37810 if (!found_initial_face_element)
37811 {
37812 std::ostringstream error_message;
37813 error_message << "Could not find an initial face element for the "
37814 "current segment\n";
37815 // << "----- Possible memory leak -----\n";
37816 throw OomphLibError(
37817 error_message.str(),
37818 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37819 OOMPH_EXCEPTION_LOCATION);
37820 }
37821#endif
37822
37823 // Local set of coordinates that are on the boundary
37824 // Set entries are ordered on first entry in vector which stores
37825 // the boundary coordinate so the vertices come out in order!
37826 std::set<Vector<double>> local_vertex_nodes;
37827
37828 // Vector to store the vertices, transfer the sorted vertices from the
37829 // set (local) to this vector (local), --- including the z-value ---
37830 Vector<Vector<double>> local_tmp_vector_vertex_node;
37831
37832 // Vector to store the target areas, uses the same approach as the
37833 // set for the local_vertex_nodes, ordered on first entry
37834 std::set<Vector<double>> sorted_target_areas;
37835
37836 // Vector to store the target areas, used to transfer the sorted target
37837 // areas from "local_sorted_target_areas" set
37838 Vector<double> tmp_sorted_target_areas;
37839
37840 // -----------------------------------------------------------------
37841 // Add the vertices of the initial face element to the set of
37842 // local sorted vertices
37843 // -----------------------------------------------------------------
37844 unsigned nnode = ele_face_pt->nnode();
37845 // Add the left-hand node to the set:
37846 // Boundary coordinate
37847 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
37848 vertex_coord[0] = bound_left[0];
37849
37850 // Actual coordinates
37851 for (unsigned i = 0; i < 2; i++)
37852 {
37853 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
37854 }
37855 local_vertex_nodes.insert(vertex_coord);
37856
37857 // Add the right-hand nodes to the set:
37858 // Boundary coordinate
37859 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
37860 bound, bound_right);
37861 vertex_coord[0] = bound_right[0];
37862
37863 // Actual coordinates
37864 for (unsigned i = 0; i < 2; i++)
37865 {
37866 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
37867 }
37868 local_vertex_nodes.insert(vertex_coord);
37869
37870 // The initial and final node on the set
37871 Node* first_node_pt = ele_face_pt->node_pt(0);
37872 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
37873
37874 // Mark the current face element as done
37875 face_element_done[ele_face_pt] = true;
37876
37877 // -------------------------------------------------------
37878 // Find the global index in the mesh of the face element
37879 // and use it to get its associated target area
37880 // -------------------------------------------------------
37881 // Container to store the zeta value (used as index) and
37882 // the associated target area of the element
37883 Vector<double> zeta_target_area_values(2);
37884
37885 // Use the minimum zeta value to sort the target areas
37886 // along the boundary
37887 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
37888
37889 // Get the index of the face element on the current boundary
37890 unsigned ef = face_element_index_on_boundary[ele_face_pt];
37891 // Get the "ef"-th element on the boundary
37892 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
37893
37894#ifdef PARANOID
37895 bool found_global_element_index = false;
37896#endif
37897 for (unsigned eg = 0; eg < nele; eg++)
37898 {
37899 // Get the "eg-th" element
37900 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
37901
37902 // Compare with the element on the boundary, if equal then
37903 // store the target area
37904 if (el_pt == el_compare_pt)
37905 {
37906 zeta_target_area_values[1] = target_area[eg];
37907#ifdef PARANOID
37908 found_global_element_index = true;
37909#endif
37910 break; // break the for (e < nele) global element
37911 } // if element_pt == element_compare_pt
37912 } // for nele (on complete mesh)
37913
37914#ifdef PARANOID
37915 if (!found_global_element_index)
37916 {
37917 std::ostringstream error_message;
37918 error_message << "The global index for the (" << ef
37919 << ")-th face element "
37920 << "on\nthe (" << bound
37921 << ")-th boundary was not found!!!";
37922 throw OomphLibError(
37923 error_message.str(),
37924 "RefineableTriangleMesh::update_polygon_using_elements_area()",
37925 OOMPH_EXCEPTION_LOCATION);
37926 }
37927#endif
37928
37929 // Add the target areas to the sorted set
37930 sorted_target_areas.insert(zeta_target_area_values);
37931 // ------------------------------------------------------------------
37932
37933 // Continue iterating if a new face element has been added to the
37934 // list
37935 bool face_element_added = false;
37936
37937 // While a new face element has been added to the set of sorted
37938 // face elements then re-iterate
37939 do
37940 {
37941 // Start from the next face elements since we have already
37942 // added the previous one as the initial face element (any
37943 // previous face element had to be added on previous
37944 // iterations)
37945 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
37946 iiface++)
37947 {
37948 face_element_added = false;
37949 ele_face_pt = non_halo_face_element_pt[iiface];
37950 if (!face_element_done[ele_face_pt])
37951 {
37952 // Get each individual node to check if they are contiguous
37953 nnode = ele_face_pt->nnode();
37954 Node* left_node_pt = ele_face_pt->node_pt(0);
37955 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
37956
37957 if (left_node_pt == first_node_pt)
37958 {
37959 first_node_pt = right_node_pt;
37960 face_element_added = true;
37961 }
37962 else if (left_node_pt == last_node_pt)
37963 {
37964 last_node_pt = right_node_pt;
37965 face_element_added = true;
37966 }
37967 else if (right_node_pt == first_node_pt)
37968 {
37969 first_node_pt = left_node_pt;
37970 face_element_added = true;
37971 }
37972 else if (right_node_pt == last_node_pt)
37973 {
37974 last_node_pt = left_node_pt;
37975 face_element_added = true;
37976 }
37977
37978 if (face_element_added)
37979 {
37980 // Add the left-hand node to the set:
37981 // Boundary coordinate
37982 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
37983 vertex_coord[0] = bound_left[0];
37984
37985 // Actual coordinates
37986 for (unsigned i = 0; i < 2; i++)
37987 {
37988 vertex_coord[i + 1] = left_node_pt->x(i);
37989 }
37990 local_vertex_nodes.insert(vertex_coord);
37991
37992 // Add the right-hand nodes to the set:
37993 // Boundary coordinate
37994 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
37995 vertex_coord[0] = bound_right[0];
37996
37997 // Actual coordinates
37998 for (unsigned i = 0; i < 2; i++)
37999 {
38000 vertex_coord[i + 1] = right_node_pt->x(i);
38001 }
38002 local_vertex_nodes.insert(vertex_coord);
38003
38004 // Mark as done only if one of its nodes has been
38005 // added to the list
38006 face_element_done[ele_face_pt] = true;
38007 nsorted_face_elements++;
38008
38009 // -----------------------------------------------------
38010 // Find the global index in the mesh of the face element
38011 // and use it to get its associated target area
38012 // -----------------------------------------------------
38013 // Use the minimum zeta value to sort the target areas
38014 // along the boundary
38015 zeta_target_area_values[0] =
38016 std::min(bound_left[0], bound_right[0]);
38017
38018 // Get the "ef"-th element on the boundary
38019 ef = face_element_index_on_boundary[ele_face_pt];
38020 FiniteElement* lel_pt = this->boundary_element_pt(bound, ef);
38021
38022#ifdef PARANOID
38023 found_global_element_index = false;
38024#endif
38025 for (unsigned eg = 0; eg < nele; eg++)
38026 {
38027 // Get the "eg-th" element
38028 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
38029
38030 // Compare with the element on the boundary, if equal then
38031 // store the target area
38032 if (lel_pt == lel_compare_pt)
38033 {
38034 zeta_target_area_values[1] = target_area[eg];
38035#ifdef PARANOID
38036 found_global_element_index = true;
38037#endif
38038 break; // break the for (e < nele) global element
38039 } // if element_pt == element_compare_pt
38040 } // for nele (on complete mesh)
38041
38042#ifdef PARANOID
38043 if (!found_global_element_index)
38044 {
38045 std::ostringstream error_message;
38046 error_message << "The global index for the (" << ef
38047 << ")-th face element "
38048 << "on\nthe (" << bound
38049 << ")-th boundary was not found!!!";
38050 throw OomphLibError(error_message.str(),
38051 "RefineableTriangleMesh::update_polygon_"
38052 "using_elements_area()",
38053 OOMPH_EXCEPTION_LOCATION);
38054 }
38055#endif
38056
38057 // Add the target areas to the sorted set
38058 sorted_target_areas.insert(zeta_target_area_values);
38059
38060 break;
38061 }
38062
38063 } // if (!edge_done[edge])
38064 } // for (iiedge < nedges)
38065 } while (face_element_added &&
38066 (nsorted_face_elements < nnon_halo_face_element));
38067
38068 // -----------------------------------------------------------------
38069 // At this point we already have a sorted set of nodes and
38070 // can be used to peform the unrefinement and refinement procedures
38071 // -----------------------------------------------------------------
38072
38073 // Get the number of nodes on the list
38074 const unsigned nlocal_nodes = local_vertex_nodes.size();
38075 // Change representation to vector for easy of handling ...
38076 local_tmp_vector_vertex_node.resize(nlocal_nodes);
38077
38078 // Copy the vertices of the nodes
38079 unsigned counter = 0;
38080 std::set<Vector<double>>::iterator it_vertex;
38081 for (it_vertex = local_vertex_nodes.begin();
38082 it_vertex != local_vertex_nodes.end();
38083 it_vertex++)
38084 {
38085 local_tmp_vector_vertex_node[counter].resize(3);
38086 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
38087 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
38088 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
38089 counter++;
38090 }
38091
38092 // ... same for the info. related with the target areas (turn
38093 // into vector)
38094 const unsigned ntarget_areas = sorted_target_areas.size();
38095 tmp_sorted_target_areas.resize(ntarget_areas);
38096 counter = 0;
38097 std::set<Vector<double>>::iterator it_area;
38098 for (it_area = sorted_target_areas.begin();
38099 it_area != sorted_target_areas.end();
38100 ++it_area)
38101 {
38102 tmp_sorted_target_areas[counter] = (*it_area)[1];
38103 ++counter;
38104 }
38105
38106#ifdef PARANOID
38107 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
38108 {
38109 std::ostringstream error_message;
38110 error_message
38111 << "The boundary (" << bound << ") was split during the "
38112 << "distribution process.\n"
38113 << "The problem is in the association of the target areas with "
38114 "the\n"
38115 << "elements that gave rise to the vertex coordinates.\n"
38116 << "The number of local nodes (" << nlocal_nodes
38117 << "), on the 'sub-polyline', is not\n"
38118 << "according with the number of target "
38119 << "areas (" << ntarget_areas << ")\nfor that number of nodes.\n"
38120 << "The target areas number MUST be equal to the number of\n"
38121 << "local nodes minus one\n\n";
38122 throw OomphLibError(error_message.str(),
38123 OOMPH_CURRENT_FUNCTION,
38124 OOMPH_EXCEPTION_LOCATION);
38125 }
38126#endif
38127
38128 // -------------------------------------------------------------------
38129 // Update the vertices along the boundary using the target area
38130 // to define the distance among them
38131 // -------------------------------------------------------------------
38132
38133 // Tolerance below which the middle point can be deleted
38134 // (ratio of deflection to element length)
38135 double unrefinement_tolerance =
38136 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38137
38138 // Apply unrefinement
38139 bool unrefinement_applied =
38140 unrefine_boundary_constrained_by_target_area(
38141 bound,
38142 chunk,
38143 local_tmp_vector_vertex_node,
38144 unrefinement_tolerance,
38145 tmp_sorted_target_areas);
38146
38147 // Tolerance for refinement
38148 double refinement_tolerance =
38149 polygon_pt->polyline_pt(p)->refinement_tolerance();
38150
38151 // Apply refinement
38152 bool refinement_applied = refine_boundary_constrained_by_target_area(
38153 mesh_geom_obj_pt,
38154 local_tmp_vector_vertex_node,
38155 refinement_tolerance,
38156 tmp_sorted_target_areas);
38157
38158 // Clear the local containter to recover the nodes ordered using the
38159 // zeta value
38160 local_vertex_nodes.clear();
38161
38162 // At the end of each unrefinement/refinement step store the new nodes
38163 // on the set that will give rise to the vertices of the new polyline
38164 // representation
38165 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
38166 for (unsigned i = 0; i < nnew_nodes; i++)
38167 {
38168 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
38169 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
38170 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
38171 vertex_nodes.insert(vertex_coord); // Global container
38172 local_vertex_nodes.insert(vertex_coord);
38173 }
38174
38175 // Update the flag to indicate whether an unrefinement or
38176 // refinement was applied
38177 update_was_performed = (unrefinement_applied || refinement_applied);
38178
38179#ifdef OOMPH_HAS_MPI
38180 if (this->is_mesh_distributed())
38181 {
38182 // Add the set of vertices for the boundary, this will help to
38183 // detect if we need to deal with sub-boundaries
38184 sub_vertex_nodes.push_back(local_vertex_nodes);
38185 // Increase the counter for sub-boundaries
38186 nsub_boundaries++;
38187 }
38188#endif
38189
38190 } // while(nsorted_face_elements < nnon_halo_face_element)
38191
38192 // Now turn into vector for ease of handling...
38193 unsigned npoly_vertex = vertex_nodes.size();
38194 // This will store all the vertices whether the boundary was split
38195 // or not
38196 tmp_vector_vertex_node.resize(npoly_vertex);
38197 unsigned count = 0;
38198 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
38199 it != vertex_nodes.end();
38200 ++it)
38201 {
38202 tmp_vector_vertex_node[count].resize(3);
38203 tmp_vector_vertex_node[count][0] = (*it)[0];
38204 tmp_vector_vertex_node[count][1] = (*it)[1];
38205 tmp_vector_vertex_node[count][2] = (*it)[2];
38206 ++count;
38207 }
38208
38209#ifdef OOMPH_HAS_MPI
38210 // --------- Stuff for the sub_boundaries ----- Begin section ---------
38211#ifdef PARANOID
38212 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
38213 if (nsub_boundaries_set != nsub_boundaries)
38214 {
38215 std::ostringstream error_message;
38216 error_message
38217 << "The number of found sub-boundaries and the number of counted\n"
38218 << "sub-boundaries are different:\n"
38219 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
38220 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
38221 throw OomphLibError(error_message.str(),
38222 OOMPH_CURRENT_FUNCTION,
38223 OOMPH_EXCEPTION_LOCATION);
38224 }
38225#endif
38226
38227 // Are there sub-boundaries (only appear in distributed meshes)
38228 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38229 {
38230 // Mark the boundary as been splitted in the partition process
38231 this->Boundary_was_splitted[bound] = true;
38232 // Resize the vector to store the info. of sub-boundaries
38233 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
38234 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38235 {
38236 // Turn info. into vector for ease of handling...
38237 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
38238 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
38239 unsigned subcount = 0;
38240 std::set<Vector<double>>::iterator subit;
38241 for (subit = sub_vertex_nodes[isub].begin();
38242 subit != sub_vertex_nodes[isub].end();
38243 ++subit)
38244 {
38245 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
38246 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
38247 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
38248 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
38249 ++subcount;
38250 }
38251 }
38252 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38253 // --------- Stuff for the sub_boundaries ----- End section ------------
38254#endif // OOMPH_HAS_MPI
38255
38256 // For further processing the three-dimensional vector has to be
38257 // reduced to a two-dimensional vector
38258 unsigned n_vertex = tmp_vector_vertex_node.size();
38259
38260 // Resize the vector for vectices
38261 vector_vertex_node.resize(n_vertex);
38262 for (unsigned i = 0; i < n_vertex; i++)
38263 {
38264 vector_vertex_node[i].resize(2);
38265 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
38266 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
38267 }
38268
38269#ifdef OOMPH_HAS_MPI
38270 // --------- Stuff for the sub_boundaries ----- Begin section ----------
38271 // Verify if need to deal with sub_boundaries
38272 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38273 {
38274 // For further processing the three-dimensional vector
38275 // has to be reduced to a two-dimensional vector
38276 // Resize the vector to store the info. of sub-boundaries
38277 sub_vector_vertex_node.resize(nsub_boundaries);
38278 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38279 {
38280 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
38281 // Resize the vector for vectices
38282 sub_vector_vertex_node[isub].resize(subn_vertex);
38283 for (unsigned i = 0; i < subn_vertex; i++)
38284 {
38285 sub_vector_vertex_node[isub][i].resize(2);
38286 sub_vector_vertex_node[isub][i][0] =
38287 sub_tmp_vector_vertex_node[isub][i][1];
38288 sub_vector_vertex_node[isub][i][1] =
38289 sub_tmp_vector_vertex_node[isub][i][2];
38290 }
38291 }
38292 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38293
38294 // We already have the info. for the sub-boundaries (if necessary)
38295 // and then we can create the sub-boundaries representations to
38296 // ease the generation of the mesh by Triangle
38297
38298 // --------- Stuff for the sub_boundaries ----- End section ------------
38299#endif // OOMPH_HAS_MPI
38300
38301 // --------------------------------------------------------------------
38302 // Check for contiguousness
38303 // --------------------------------------------------------------------
38304#ifdef OOMPH_HAS_MPI
38305 // Only perform this checking if the mesh is not distributed. When
38306 // the mesh is distributed the polylines continuity is addressed
38307 // by the sort_polylines_helper() method
38308 if (!this->is_mesh_distributed())
38309#endif
38310 {
38311 if (p > 0)
38312 {
38313 // Final end point of previous line
38314 Vector<double> final_vertex_of_previous_segment;
38315 unsigned n_prev_vertex =
38316 polygon_pt->curve_section_pt(p - 1)->nvertex();
38317 final_vertex_of_previous_segment =
38318 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
38319 1);
38320
38321 unsigned prev_seg_boundary_id =
38322 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38323
38324 // Find the error between the final vertex of the previous
38325 // line and the first vertex of the current line
38326 double error = 0.0;
38327 for (unsigned i = 0; i < 2; i++)
38328 {
38329 const double dist = final_vertex_of_previous_segment[i] -
38330 (*vector_vertex_node.begin())[i];
38331 error += dist * dist;
38332 }
38333 error = sqrt(error);
38334
38335 // If the error is bigger than the tolerance then
38336 // we probably need to reverse, but better check
38338 {
38339 // Find the error between the final vertex of the previous
38340 // line and the last vertex of the current line
38341 double rev_error = 0.0;
38342 for (unsigned i = 0; i < 2; i++)
38343 {
38344 const double dist = final_vertex_of_previous_segment[i] -
38345 (*--vector_vertex_node.end())[i];
38346 rev_error += dist * dist;
38347 }
38348 rev_error = sqrt(rev_error);
38349
38350 if (rev_error >
38352 {
38353 // It could be possible that the first segment be reversed
38354 // and we did not notice it because this check does not
38355 // apply for the first segment. We can verify if the first
38356 // segment is reversed by using the vertex number 1
38357 if (p == 1)
38358 {
38359 // Initial end point of previous line
38360 Vector<double> initial_vertex_of_previous_segment;
38361
38362 initial_vertex_of_previous_segment =
38363 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
38364
38365 unsigned prev_seg_boundary_id =
38366 polygon_pt->curve_section_pt(p - 1)->boundary_id();
38367
38368 // Find the error between the initial vertex of the previous
38369 // line and the first vertex of the current line
38370 double error = 0.0;
38371 for (unsigned i = 0; i < 2; i++)
38372 {
38373 const double dist = initial_vertex_of_previous_segment[i] -
38374 (*vector_vertex_node.begin())[i];
38375 error += dist * dist;
38376 }
38377 error = sqrt(error); // Reversed only the previous one
38378
38379 // If the error is bigger than the tolerance then
38380 // we probably need to reverse, but better check
38381 if (error >
38383 {
38384 // Find the error between the final vertex of the previous
38385 // line and the last vertex of the current line
38386 double rev_error = 0.0;
38387 for (unsigned i = 0; i < 2; i++)
38388 {
38389 const double dist = initial_vertex_of_previous_segment[i] -
38390 (*--vector_vertex_node.end())[i];
38391 rev_error += dist * dist;
38392 }
38393 rev_error =
38394 sqrt(rev_error); // Reversed both the current one and
38395 // the previous one
38396
38397 if (rev_error >
38399 {
38400 std::ostringstream error_stream;
38401 error_stream
38402 << "The distance between the first node of the current\n"
38403 << "line segment (boundary " << bound
38404 << ") and either end of "
38405 << "the previous line segment\n"
38406 << "(boundary " << prev_seg_boundary_id
38407 << ") is bigger than "
38408 << "the desired tolerance "
38410 << ".\n"
38411 << "This suggests that the polylines defining the "
38412 << "polygonal\n"
38413 << "representation are not properly ordered.\n"
38414 << "Fail on last vertex of polyline: ("
38415 << prev_seg_boundary_id << ") and\n"
38416 << "first vertex of polyline (" << bound << ").\n"
38417 << "This should have failed when first trying to "
38418 << "construct the\npolygon.\n";
38419 throw OomphLibError(error_stream.str(),
38420 OOMPH_CURRENT_FUNCTION,
38421 OOMPH_EXCEPTION_LOCATION);
38422 }
38423 else
38424 {
38425 // Reverse both
38426 // Reverse the current vector to line up with the
38427 // previous one
38428 std::reverse(vector_vertex_node.begin(),
38429 vector_vertex_node.end());
38430
38431 polygon_pt->polyline_pt(p - 1)->reverse();
38432 }
38433 }
38434 else
38435 {
38436 // Reverse the previous one
38437 polygon_pt->polyline_pt(p - 1)->reverse();
38438 }
38439
38440 } // if p == 1
38441 else
38442 {
38443 std::ostringstream error_stream;
38444 error_stream
38445 << "The distance between the first node of the current\n"
38446 << "line segment (boundary " << bound
38447 << ") and either end of "
38448 << "the previous line segment\n"
38449 << "(boundary " << prev_seg_boundary_id
38450 << ") is bigger than the "
38451 << "desired tolerance "
38453 << ".\n"
38454 << "This suggests that the polylines defining the polygonal\n"
38455 << "representation are not properly ordered.\n"
38456 << "Fail on last vertex of polyline: ("
38457 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
38458 << bound << ").\n"
38459 << "This should have failed when first trying to construct"
38460 << " the polygon.\n";
38461 throw OomphLibError(error_stream.str(),
38462 OOMPH_CURRENT_FUNCTION,
38463 OOMPH_EXCEPTION_LOCATION);
38464 }
38465 }
38466 else
38467 {
38468 // Reverse the current vector to line up with the previous one
38469 std::reverse(vector_vertex_node.begin(),
38470 vector_vertex_node.end());
38471 }
38472 } // error
38473
38474 } // if ( p > 0 )
38475
38476 } // if (!this->is_mesh_distributed())
38477
38478 // --------------------------------------------------------------------
38479 // Update the polylines representation
38480 // --------------------------------------------------------------------
38481
38482 // Always update the polylines representation, in a distributed
38483 // mesh it is necessary to update the polyline representation since
38484 // it may no longer have vertices (the boundary may not be part of
38485 // the domain in the current processor)
38486
38487 // The new nunber of vertices
38488 n_vertex = vector_vertex_node.size();
38489
38490 // Now update the polyline according to the new vertices
38491 TriangleMeshPolyLine* tmp_polyline_pt =
38492 new TriangleMeshPolyLine(vector_vertex_node, bound);
38493
38494 // Create a temporal "curve section" version of the recently
38495 // created polyline
38496 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
38497
38498 // Tolerance below which the middle point can be deleted (ratio of
38499 // deflection to element length)
38500 double unrefinement_tolerance =
38501 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
38502
38503 // Tolerance to add points
38504 double refinement_tolerance =
38505 polygon_pt->polyline_pt(p)->refinement_tolerance();
38506
38507 // Establish refinement and unrefinement tolerance
38508 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
38509 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
38510
38511 // Establish the maximum length constraint
38512 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
38513 tmp_polyline_pt->set_maximum_length(maximum_length);
38514
38515#ifdef OOMPH_HAS_MPI
38516 // If the mesh is distributed check that the polyline still has
38517 // vertices
38518 if (this->is_mesh_distributed())
38519 {
38520 if (n_vertex >= 2)
38521 {
38522 // Pass the connection information from the old polyline to the
38523 // new one
38524 this->copy_connection_information(polygon_pt->polyline_pt(p),
38525 tmp_curve_section_pt);
38526 } // if (n_vertex >= 2)
38527 } // if (this->is_mesh_distributed())
38528 else
38529#endif
38530 {
38531 // Pass the connection information from the old polyline to the
38532 // new one
38533 this->copy_connection_information(polygon_pt->polyline_pt(p),
38534 tmp_curve_section_pt);
38535 }
38536
38537 // Now update the polyline according to the new vertices but first
38538 // check if the object is allowed to delete the representation or
38539 // if it should be done by other object
38540 bool delete_it_on_destructor = false;
38541
38542 std::set<TriangleMeshCurveSection*>::iterator it =
38543 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
38544
38545 if (it != this->Free_curve_section_pt.end())
38546 {
38547 this->Free_curve_section_pt.erase(it);
38548 delete polygon_pt->curve_section_pt(p);
38549 delete_it_on_destructor = true;
38550 }
38551
38552 // -------------------------------------------------------
38553 // Copying the new representation
38554 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
38555
38556 // Update the Boundary - Polyline map
38557 this->Boundary_curve_section_pt[bound] = polygon_pt->curve_section_pt(p);
38558
38559 if (delete_it_on_destructor)
38560 {
38561 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
38562 }
38563
38564#ifdef OOMPH_HAS_MPI
38565 // --------- Stuff for the sub_boundaries ----- Begin section --------
38566 // Verify if need to deal with sub_boundaries
38567 if (this->is_mesh_distributed() && nsub_boundaries > 1)
38568 {
38569 // Create temporary representations for the boundaries, only to
38570 // create the mesh when calling Triangle
38571
38572 // Clear all previous stored data
38573 this->Boundary_subpolylines[bound].clear();
38574
38575 // Create storage for the sub-boundaries
38576 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
38577 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
38578 {
38579 // Update the polyline according to the sub set of vertices,
38580 TriangleMeshPolyLine* sub_tmp_polyline_pt =
38581 new TriangleMeshPolyLine(sub_vector_vertex_node[isub], bound, isub);
38582
38583 // Add the sub-polyline to the container to represent the
38584 // boundary in parts
38585 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
38586
38587 // No need to send the unrefinement/refinement and maximum
38588 // length constraints since these are only temporary
38589 // representations. These polylines can be deleted once the new
38590 // polygons that represent the distributed domain have been
38591 // created
38592
38593 } // for (isub < nsub_boundaries)
38594
38595 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
38596 // --------- Stuff for the sub_boundaries ----- End section ---------
38597#endif // OOMPH_HAS_MPI
38598
38599 // Delete the allocated memory for the geometric object that
38600 // represents the boundary
38601 delete mesh_geom_obj_pt;
38602
38603 } // for (p < n_polyline)
38604
38605 // Cleanup the face mesh
38606 for (unsigned p = 0; p < n_polyline; p++)
38607 {
38608 face_mesh_pt[p]->flush_node_storage();
38609 delete face_mesh_pt[p];
38610 }
38611
38612 return update_was_performed;
38613 }
38614
38615 //======================================================================
38616 /// Updates the open curve but using the elements area instead
38617 /// of the default refinement and unrefinement methods
38618 //======================================================================
38619 template<class ELEMENT>
38621 TriangleMeshOpenCurve*& open_curve_pt, const Vector<double>& target_area)
38622 {
38623 // Verify if there was a change on the open curve representation
38624 unsigned update_was_performed = false;
38625
38626 const unsigned nele = this->nelement();
38627
38628 // - Get the vertices along the boundaries and for each element identify
38629 // its associated target error.
38630 // - Get face mesh representation of each polyline.
38631 // - Get the vertices with the help of face elements.
38632 // - Find the global index in the mesh of the face element
38633 // and use it to get its associated target area.
38634
38635 // Get the face mesh representation
38636 Vector<Mesh*> face_mesh_pt;
38637 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
38638
38639 // Create vertices of the polylines by using the vertices of the
38640 // FaceElements
38641 Vector<double> vertex_coord(3); // zeta,x,y
38642 Vector<double> bound_left(1);
38643 Vector<double> bound_right(1);
38644
38645 const unsigned ncurve_section = open_curve_pt->ncurve_section();
38646
38647 // Go for each curve section
38648 for (unsigned cs = 0; cs < ncurve_section; cs++)
38649 {
38650 // Get the MeshAsGeomObject representation just once per polyline,
38651 // this object is only used by the
38652 // refine_boundary_constrained_by_target_area() method. We get it
38653 // here to ensure that all processors (in a distributed context)
38654 // get this representation just once, and because an AllToAll MPI
38655 // communication is used in this calling
38656 MeshAsGeomObject* mesh_geom_obj_pt =
38657 new MeshAsGeomObject(face_mesh_pt[cs]);
38658
38659 // Get the boundary id
38660 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
38661
38662 // Get the chunk number
38663 const unsigned chunk =
38664 open_curve_pt->curve_section_pt(cs)->boundary_chunk();
38665
38666 /// Use a vector of vector for vertices and target areas to deal
38667 /// with the cases when the boundaries are split by the
38668 /// distribution process. Internal boundaries may be completely or
38669 /// partially overlapped by shared boundaries
38670
38671 // Loop over the face elements and add their vertices (they are
38672 // automatically sorted because of the set)
38673 const unsigned nface_element = face_mesh_pt[cs]->nelement();
38674
38675 // Store the non halo elements and the element at the other side of
38676 // the boundary (whatever it be halo or not), the first will be the
38677 // ones from which we will get the vertices (in even position)
38678 Vector<FiniteElement*> non_halo_doubled_face_element_pt;
38679
38680 // Map to store the index of the face element on a boundary
38681 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
38682
38683 // Map to know the already sorted face elements
38684 std::map<FiniteElement*, bool> face_element_done;
38685
38686 for (unsigned ef = 0; ef < nface_element; ++ef)
38687 {
38688 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
38689
38690 // Skip the halo elements (not used as base elements, only
38691 // include those elements whose element at the other side of the
38692 // boundary is non halo)
38693#ifdef OOMPH_HAS_MPI
38694 if (this->is_mesh_distributed())
38695 {
38696 // Only work with non-halo elements
38697 if (ele_face_pt->is_halo())
38698 {
38699 continue;
38700 }
38701 }
38702#endif
38703
38704 // Check if not already done
38705 if (!face_element_done[ele_face_pt])
38706 {
38707 // Add the element and look for the element at the other side
38708 // of the boundary to add it immediately after the new added
38709 // element
38710 non_halo_doubled_face_element_pt.push_back(ele_face_pt);
38711 // Create the map of the face element with the index
38712 face_element_index_on_boundary[ele_face_pt] = ef;
38713 // Mark the current element as done
38714 face_element_done[ele_face_pt] = true;
38715 // Get the number of nodes
38716 const unsigned nnodes = ele_face_pt->nnode();
38717 // Get the left and right node to look for the elements at the
38718 // other side of the boundary
38719 Node* left_node_pt = ele_face_pt->node_pt(0);
38720 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
38721#ifdef PARANOID
38722 // Flag to know if the element at the other side of the
38723 // boundary was found
38724 bool found_other_side_face_ele = false;
38725#endif
38726 for (unsigned iface = 0; iface < nface_element; iface++)
38727 {
38728 // Get the candidate face element
38729 FiniteElement* cele_face_pt =
38730 face_mesh_pt[cs]->finite_element_pt(iface);
38731 // Check if not already done
38732 if (!face_element_done[cele_face_pt])
38733 {
38734 Node* cleft_node_pt = cele_face_pt->node_pt(0);
38735 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
38736 // Check if the nodes are the same
38737 if ((left_node_pt == cleft_node_pt &&
38738 right_node_pt == cright_node_pt) ||
38739 (left_node_pt == cright_node_pt &&
38740 right_node_pt == cleft_node_pt))
38741 {
38742 // Add the element to the storage
38743 non_halo_doubled_face_element_pt.push_back(cele_face_pt);
38744 // ... and mark the element as done
38745 face_element_done[cele_face_pt] = true;
38746 // Create the map of the face element with the index
38747 face_element_index_on_boundary[cele_face_pt] = iface;
38748#ifdef PARANOID
38749 // Set the flag of found other side face element
38750 found_other_side_face_ele = true;
38751#endif
38752 break;
38753 }
38754 }
38755 } // (iface < nface_element)
38756
38757#ifdef PARANOID
38758 if (!found_other_side_face_ele)
38759 {
38760 std::ostringstream error_message;
38761 error_message
38762 << "The face element at the other side of the boundary (" << bound
38763 << ") was not found!!\n"
38764 << "These are the nodes of the face element:\n"
38765 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
38766 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
38767 << ")\n\n";
38768 throw OomphLibError(
38769 error_message.str(),
38770 "RefineableTriangleMesh::update_open_curve_using_elements_area()",
38771 OOMPH_EXCEPTION_LOCATION);
38772 }
38773#endif
38774 } // if (!face_ele_done[ele_face_pt])
38775
38776 } // (ef < nface_element)
38777
38778 // Clear the map of the already done face elements
38779 // This will be used to help sorting the face elements
38780 face_element_done.clear();
38781
38782 // Set of coordinates that are on the boundary
38783 // The entries are sorted on first entry in vector which stores
38784 // the boundary coordinate so the vertices come out in order!
38785 std::set<Vector<double>> vertex_nodes;
38786
38787 // Vector to store the vertices, transfer the sorted vertices from the
38788 // set to this vector, --- including the z-value ---
38789 Vector<Vector<double>> tmp_vector_vertex_node;
38790
38791 // Vector to store the coordinates of the polylines, same as the
38792 // tmp_vector_vertex_node vector (after adding more nodes) but
38793 // --- without the z-value ---, used to re-generate the polylines
38794 Vector<Vector<double>> vector_vertex_node;
38795
38796#ifdef OOMPH_HAS_MPI
38797 // Indicates if the set of vertices give rise to a internal
38798 // boundary that will be used as shared boundary or as normal
38799 // internal boundary -- Only used to deal with internal boundaries
38800 // in a distributed scheme
38801 std::vector<bool> internal_to_shared_boundary;
38802
38803 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
38804 // Set of coordinates that are on the boundary (splitted boundary version)
38805 // The first vector is used to allocate the points for each sub-boundary
38806 // Set entries are ordered on first entry in vector which stores
38807 // the boundary coordinate so the vertices come out in order!
38808 Vector<std::set<Vector<double>>> sub_vertex_nodes;
38809
38810 // Vector to store the vertices, transfer the sorted vertices from the
38811 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
38812 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
38813
38814 // Vector to store the coordinates of the polylines that will represent
38815 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
38816 // but --- without the z-value ---, used to generate the sub-polylines
38817 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
38818
38819 // --------- Stuff to deal with splitted boundaries ----------- End ------
38820
38821#endif // #ifdef OOMPH_HAS_MPI
38822
38823 // Sort the face element, those that have both elements (one at
38824 // each side of the boundary) marked as nonhalo, and those with one
38825 // nonhalo an the other as halo
38826
38827 // Number of done face elements
38828 unsigned nsorted_face_elements = 0;
38829
38830#ifdef OOMPH_HAS_MPI
38831 // Counter for sub_boundaries
38832 unsigned nsub_boundaries = 0;
38833#endif // #ifdef OOMPH_HAS_MPI
38834
38835 // Total number of non halo double face element
38836 const unsigned nnon_halo_doubled_face_ele =
38837 non_halo_doubled_face_element_pt.size();
38838
38839 // Continue until all the face elements have been sorted
38840 // This while is to deal with the cases of splitted boundaries
38841 while (nsorted_face_elements < nnon_halo_doubled_face_ele)
38842 {
38843 // Get and initial face element
38844 FiniteElement* ele_face_pt = 0;
38845 FiniteElement* repeated_ele_face_pt = 0;
38846#ifdef PARANOID
38847 bool found_initial_face_element = false;
38848#endif
38849
38850 // Flag to know if we are working with a face element which the
38851 // face element at the other side of the boundary is also non
38852 // halo
38853 bool both_root_face_elements_are_nonhalo = false;
38854
38855 unsigned iface = 0;
38856 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
38857 {
38858 ele_face_pt = non_halo_doubled_face_element_pt[iface];
38859 // If not done then take it as initial face element
38860 if (!face_element_done[ele_face_pt])
38861 {
38862 // Mark it as done
38863 face_element_done[ele_face_pt] = true;
38864 // Get the other side boundary face element
38865 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iface + 1];
38866 // ... also mark as done the repeated face element
38867 face_element_done[repeated_ele_face_pt] = true;
38868
38869#ifdef OOMPH_HAS_MPI
38870 if (!repeated_ele_face_pt->is_halo())
38871 {
38872 both_root_face_elements_are_nonhalo = true;
38873 }
38874#endif // #ifdef OOMPH_HAS_MPI
38875
38876 // Plus two because internal boundaries have
38877 // two face elements per each edge
38878 nsorted_face_elements += 2;
38879 iface += 2;
38880#ifdef PARANOID
38881 // And set the flag to true
38882 found_initial_face_element = true;
38883#endif
38884 break;
38885 }
38886 }
38887
38888#ifdef PARANOID
38889 if (!found_initial_face_element)
38890 {
38891 std::ostringstream error_message;
38892 error_message << "Could not find an initial face element for the "
38893 "current segment\n";
38894 throw OomphLibError(error_message.str(),
38895 OOMPH_CURRENT_FUNCTION,
38896 OOMPH_EXCEPTION_LOCATION);
38897 }
38898#endif
38899
38900 // Local set of coordinates that are on the boundary Set entries
38901 // are ordered on first entry in vector which stores the boundary
38902 // coordinate so the vertices come out in order
38903 std::set<Vector<double>> local_vertex_nodes;
38904
38905 // Vector to store the vertices, transfer the sorted vertices from the
38906 // set (local) to this vector (local), --- including the z-value ---
38907 Vector<Vector<double>> local_tmp_vector_vertex_node;
38908
38909 // Vector to store the target areas, uses the same approach as the
38910 // set for the local_vertex_nodes, ordered on first entry
38911 std::set<Vector<double>> sorted_target_areas;
38912
38913 // Vector to store the target areas, used to transfer the sorted target
38914 // areas from "sorted_target_areas" set
38915 Vector<double> tmp_sorted_target_areas;
38916
38917 // ------------------------------------------------------------------
38918 // Add the vertices of the initial face element to the set of local
38919 // sorted vertices
38920 // ------------------------------------------------------------------
38921 const unsigned nnode = ele_face_pt->nnode();
38922 // Add the left-hand node to the set:
38923 // Boundary coordinate
38924 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
38925 vertex_coord[0] = bound_left[0];
38926
38927 // Actual coordinates
38928 for (unsigned i = 0; i < 2; i++)
38929 {
38930 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
38931 }
38932 local_vertex_nodes.insert(vertex_coord);
38933
38934 // Add the right-hand node to the set:
38935 // Boundary coordinate
38936 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
38937 bound, bound_right);
38938 vertex_coord[0] = bound_right[0];
38939
38940 // Actual coordinates
38941 for (unsigned i = 0; i < 2; i++)
38942 {
38943 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
38944 }
38945 local_vertex_nodes.insert(vertex_coord);
38946
38947 // The initial and final node on the set
38948 Node* first_node_pt = ele_face_pt->node_pt(0);
38949 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
38950
38951 // -----------------------------------------------------
38952 // Find the global index in the mesh of the face element
38953 // and use it to get its associated target area
38954 // -----------------------------------------------------
38955 // Container to store the zeta value (used as index) and
38956 // the associated target area of the element
38957 Vector<double> zeta_target_area_values(2);
38958
38959 // Use the minimum zeta value to sort the target areas
38960 // along the boundary
38961 zeta_target_area_values[0] = std::min(bound_left[0], bound_right[0]);
38962
38963 // Get the index of the face element on the current boundary
38964 const unsigned ef = face_element_index_on_boundary[ele_face_pt];
38965 // Get the "ef"-th element on the boundary
38966 FiniteElement* el_pt = this->boundary_element_pt(bound, ef);
38967 double target_area_face_element = 0.0;
38968
38969#ifdef PARANOID
38970 bool found_global_element_index = false;
38971#endif
38972 for (unsigned eg = 0; eg < nele; eg++)
38973 {
38974 // Get the "eg-th" element
38975 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
38976
38977 // Compare with the element on the boundary, if equal then
38978 // store the target area
38979 if (el_pt == el_compare_pt)
38980 {
38981 target_area_face_element = target_area[eg];
38982#ifdef PARANOID
38983 found_global_element_index = true;
38984#endif
38985 break; // break the for (eg < nele) global element
38986 } // if el_pt == el_compare_pt
38987 } // for nele (on complete mesh)
38988
38989#ifdef PARANOID
38990 if (!found_global_element_index)
38991 {
38992 std::ostringstream error_message;
38993 error_message << "The global index for the (" << ef
38994 << ")-th face element "
38995 << "on\nthe (" << bound
38996 << ")-th boundary was not found!!!";
38997 throw OomphLibError(error_message.str(),
38998 OOMPH_CURRENT_FUNCTION,
38999 OOMPH_EXCEPTION_LOCATION);
39000 }
39001#endif
39002
39003 // Get the index of the repeated face element on the current boundary
39004 const unsigned ref =
39005 face_element_index_on_boundary[repeated_ele_face_pt];
39006 FiniteElement* rel_pt = this->boundary_element_pt(bound, ref);
39007 double target_area_repeated_face_element = 0.0;
39008
39009#ifdef PARANOID
39010 bool found_global_repeated_element_index = false;
39011#endif
39012 for (unsigned eg = 0; eg < nele; eg++)
39013 {
39014 // Get the "eg-th" element
39015 FiniteElement* el_compare_pt = this->finite_element_pt(eg);
39016
39017 // Compare with the element on the boundary, if equal then
39018 // store the target area
39019 if (rel_pt == el_compare_pt)
39020 {
39021 target_area_repeated_face_element = target_area[eg];
39022#ifdef PARANOID
39023 found_global_repeated_element_index = true;
39024#endif
39025 break; // break the for (eg < nele) global element
39026 } // if rel_pt == el_compare_pt
39027 } // for nele (on complete mesh)
39028
39029#ifdef PARANOID
39030 if (!found_global_repeated_element_index)
39031 {
39032 std::ostringstream error_message;
39033 error_message << "The global index for the (" << ref
39034 << ")-th face element "
39035 << "on\nthe (" << bound
39036 << ")-th boundary was not found (repeated "
39037 << "face element)!!!";
39038 throw OomphLibError(error_message.str(),
39039 OOMPH_CURRENT_FUNCTION,
39040 OOMPH_EXCEPTION_LOCATION);
39041 }
39042#endif
39043
39044 // Choose the minimum target area from both elements, one at each side
39045 // of the edge on the boundary
39046 zeta_target_area_values[1] =
39047 std::min(target_area_face_element, target_area_repeated_face_element);
39048
39049 // Add the target areas to the sorted set
39050 sorted_target_areas.insert(zeta_target_area_values);
39051 // ------------------------------------------------------------------
39052
39053 // Continue iterating if a new face element has been added to the
39054 // list
39055 bool face_element_added = false;
39056
39057 // While a new face element has been added to the set of sorted
39058 // face elements then re-iterate
39059 do
39060 {
39061 // Start from the next face elements since we have already
39062 // added the previous one as the initial face element (any
39063 // previous face element had to be added on previous
39064 // iterations)
39065 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
39066 iiface += 2)
39067 {
39068 face_element_added = false;
39069 ele_face_pt = non_halo_doubled_face_element_pt[iiface];
39070
39071 // Check that the face element with which we are working has
39072 // the same conditions as the root face element (both faces
39073 // are nonhalo or one face is halo and the other nonhalo)
39074
39075 // Get the face element at the other side of the boundary
39076 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iiface + 1];
39077 bool both_face_elements_are_nonhalo = false;
39078
39079#ifdef OOMPH_HAS_MPI
39080 if (!repeated_ele_face_pt->is_halo())
39081 {
39082 both_face_elements_are_nonhalo = true;
39083 }
39084#endif // #ifdef OOMPH_HAS_MPI
39085
39086 if (!face_element_done[ele_face_pt] &&
39087 (both_face_elements_are_nonhalo ==
39088 both_root_face_elements_are_nonhalo))
39089 {
39090 // Get each individual node to check if they are contiguous
39091 const unsigned nlnode = ele_face_pt->nnode();
39092 Node* left_node_pt = ele_face_pt->node_pt(0);
39093 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
39094
39095 if (left_node_pt == first_node_pt)
39096 {
39097 first_node_pt = right_node_pt;
39098 face_element_added = true;
39099 }
39100 else if (left_node_pt == last_node_pt)
39101 {
39102 last_node_pt = right_node_pt;
39103 face_element_added = true;
39104 }
39105 else if (right_node_pt == first_node_pt)
39106 {
39107 first_node_pt = left_node_pt;
39108 face_element_added = true;
39109 }
39110 else if (right_node_pt == last_node_pt)
39111 {
39112 last_node_pt = left_node_pt;
39113 face_element_added = true;
39114 }
39115
39116 if (face_element_added)
39117 {
39118 // Add the left-hand node to the set:
39119 // Boundary coordinate
39120 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
39121 vertex_coord[0] = bound_left[0];
39122
39123 // Actual coordinates
39124 for (unsigned i = 0; i < 2; i++)
39125 {
39126 vertex_coord[i + 1] = left_node_pt->x(i);
39127 }
39128 local_vertex_nodes.insert(vertex_coord);
39129
39130 // Add the right-hand nodes to the set:
39131 // Boundary coordinate
39132 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
39133 vertex_coord[0] = bound_right[0];
39134
39135 // Actual coordinates
39136 for (unsigned i = 0; i < 2; i++)
39137 {
39138 vertex_coord[i + 1] = right_node_pt->x(i);
39139 }
39140 local_vertex_nodes.insert(vertex_coord);
39141
39142 // Mark as done only if one of its nodes has been
39143 // added to the list
39144 face_element_done[ele_face_pt] = true;
39145 // .. also mark as done the face element at the othe side of
39146 // the boundary
39147 repeated_ele_face_pt =
39148 non_halo_doubled_face_element_pt[iiface + 1];
39149 face_element_done[repeated_ele_face_pt] = true;
39150 // ... and increase the number of sorted face elements
39151 nsorted_face_elements += 2;
39152
39153 // -----------------------------------------------------
39154 // Find the global index in the mesh of the face element
39155 // and use it to get its associated target area
39156 // -----------------------------------------------------
39157 // Use the minimum zeta value to sort the target areas
39158 // along the boundary
39159 zeta_target_area_values[0] =
39160 std::min(bound_left[0], bound_right[0]);
39161
39162 // Get the "ef"-th element on the boundary
39163 const unsigned lef =
39164 face_element_index_on_boundary[ele_face_pt];
39165 FiniteElement* lel_pt = this->boundary_element_pt(bound, lef);
39166
39167#ifdef PARANOID
39168 found_global_element_index = false;
39169#endif
39170 for (unsigned eg = 0; eg < nele; eg++)
39171 {
39172 // Get the "eg-th" element
39173 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39174
39175 // Compare with the element on the boundary, if equal then
39176 // store the target area
39177 if (lel_pt == lel_compare_pt)
39178 {
39179 target_area_face_element = target_area[eg];
39180#ifdef PARANOID
39181 found_global_element_index = true;
39182#endif
39183 break; // break the for (eg < nele) global element
39184 } // if lel_pt == lel_compare_pt
39185 } // for nele (on complete mesh)
39186
39187#ifdef PARANOID
39188 if (!found_global_element_index)
39189 {
39190 std::ostringstream error_message;
39191 error_message << "The global index for the (" << lef
39192 << ")-th face element "
39193 << "on\nthe (" << bound
39194 << ")-th boundary was not found!!!";
39195 throw OomphLibError(error_message.str(),
39196 OOMPH_CURRENT_FUNCTION,
39197 OOMPH_EXCEPTION_LOCATION);
39198 }
39199#endif
39200
39201 // Get the index of the repeated face element on the boundary
39202 const unsigned rlef =
39203 face_element_index_on_boundary[repeated_ele_face_pt];
39204 FiniteElement* rlel_pt = this->boundary_element_pt(bound, rlef);
39205
39206#ifdef PARANOID
39207 found_global_repeated_element_index = false;
39208#endif
39209 for (unsigned eg = 0; eg < nele; eg++)
39210 {
39211 // Get the "eg-th" element
39212 FiniteElement* lel_compare_pt = this->finite_element_pt(eg);
39213
39214 // Compare with the element on the boundary, if equal then
39215 // store the target area
39216 if (rlel_pt == lel_compare_pt)
39217 {
39218 target_area_repeated_face_element = target_area[eg];
39219#ifdef PARANOID
39220 found_global_repeated_element_index = true;
39221#endif
39222 break; // break the for (eg < nele) global element
39223 } // if rlel_pt == el_compare_pt
39224 } // for nele (on complete mesh)
39225
39226#ifdef PARANOID
39227 if (!found_global_repeated_element_index)
39228 {
39229 std::ostringstream error_message;
39230 error_message << "The global index for the (" << rlef
39231 << ")-th face element "
39232 << "on\nthe (" << bound
39233 << ")-th boundary was not found "
39234 << "(repeated face element)!!!";
39235 throw OomphLibError(error_message.str(),
39236 OOMPH_CURRENT_FUNCTION,
39237 OOMPH_EXCEPTION_LOCATION);
39238 }
39239#endif
39240
39241 // Choose the minimum target area from both elements, one
39242 // at each side of the edge on the boundary
39243 zeta_target_area_values[1] = std::min(
39244 target_area_face_element, target_area_repeated_face_element);
39245
39246 // Add the target areas to the sorted set
39247 sorted_target_areas.insert(zeta_target_area_values);
39248
39249 break;
39250 }
39251
39252 } // if (!face_element_done[[ele_face_pt])
39253 } // for (iiface<nnon_halo_doubled_face_ele)
39254 } while (face_element_added &&
39255 (nsorted_face_elements < nnon_halo_doubled_face_ele));
39256
39257 // -------------------------------------------------------------
39258 // At this point we already have a sorted set of nodes and can
39259 // be used to peform the unrefinement and refinement procedures
39260 // -------------------------------------------------------------
39261
39262 // Get the number of nodes on the list
39263 const unsigned nlocal_nodes = local_vertex_nodes.size();
39264 // Change representation to vector for easy of handling ...
39265 local_tmp_vector_vertex_node.resize(nlocal_nodes);
39266
39267 // Copy the vertices of the nodes
39268 unsigned counter = 0;
39269 std::set<Vector<double>>::iterator it_vertex;
39270 for (it_vertex = local_vertex_nodes.begin();
39271 it_vertex != local_vertex_nodes.end();
39272 it_vertex++)
39273 {
39274 local_tmp_vector_vertex_node[counter].resize(3);
39275 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
39276 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
39277 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
39278 counter++;
39279 }
39280
39281 // ... same for the info. related with the target areas (turn
39282 // into vector)
39283 const unsigned ntarget_areas = sorted_target_areas.size();
39284 tmp_sorted_target_areas.resize(ntarget_areas);
39285 counter = 0;
39286 std::set<Vector<double>>::iterator it_area;
39287 for (it_area = sorted_target_areas.begin();
39288 it_area != sorted_target_areas.end();
39289 ++it_area)
39290 {
39291 tmp_sorted_target_areas[counter] = (*it_area)[1];
39292 ++counter;
39293 }
39294
39295#ifdef PARANOID
39296 if (nlocal_nodes > 0 && (ntarget_areas != nlocal_nodes - 1))
39297 {
39298 std::ostringstream error_message;
39299 error_message
39300 << "The boundary (" << bound << ") was split during the "
39301 << "distribution process.\n"
39302 << "The problem comes when associating the target areas with the "
39303 << "elements that gave\nrise to the vertex coordinates.\n"
39304 << "The number of local nodes on the 'sub-polyline' ("
39305 << nlocal_nodes << ") is not according with the number of target\n"
39306 << "areas (" << ntarget_areas << ") for that number of nodes.\n"
39307 << "The target areas number must be equal to the number of "
39308 "nodes-1\n";
39309 throw OomphLibError(error_message.str(),
39310 OOMPH_CURRENT_FUNCTION,
39311 OOMPH_EXCEPTION_LOCATION);
39312 }
39313#endif
39314
39315 // The unrefinement and refinement process needs to be applied
39316 // from the bottom-left node since the internal open curve could
39317 // lie on the shared boundaries
39318 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] <
39319 local_tmp_vector_vertex_node[0][2])
39320 {
39321 std::reverse(local_tmp_vector_vertex_node.begin(),
39322 local_tmp_vector_vertex_node.end());
39323 std::reverse(tmp_sorted_target_areas.begin(),
39324 tmp_sorted_target_areas.end());
39325 }
39326 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
39327 local_tmp_vector_vertex_node[0][2])
39328 {
39329 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][1] <
39330 local_tmp_vector_vertex_node[0][1])
39331 {
39332 std::reverse(local_tmp_vector_vertex_node.begin(),
39333 local_tmp_vector_vertex_node.end());
39334 std::reverse(tmp_sorted_target_areas.begin(),
39335 tmp_sorted_target_areas.end());
39336 }
39337 }
39338
39339 // ------------------------------------------------------------
39340 // Create the vertices along the boundary using the target
39341 // area to define the distance among them
39342 // ------------------------------------------------------------
39343
39344 // Tolerance below which the middle point can be deleted
39345 // (ratio of deflection to element length)
39346 double unrefinement_tolerance =
39347 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39348
39349 // Apply unrefinement
39350 bool unrefinement_applied =
39351 unrefine_boundary_constrained_by_target_area(
39352 bound,
39353 chunk,
39354 local_tmp_vector_vertex_node,
39355 unrefinement_tolerance,
39356 tmp_sorted_target_areas);
39357
39358 // Tolerance for refinement
39359 double refinement_tolerance =
39360 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39361
39362 // Apply refinement
39363 bool refinement_applied = refine_boundary_constrained_by_target_area(
39364 mesh_geom_obj_pt,
39365 local_tmp_vector_vertex_node,
39366 refinement_tolerance,
39367 tmp_sorted_target_areas);
39368
39369 // Clear the local containter to recover the nodes ordered using
39370 // the zeta value
39371 local_vertex_nodes.clear();
39372
39373 // At the end of each unrefinement/refinement step store the new
39374 // nodes on the set that will give rise to the vertices of the
39375 // new polyline representation
39376 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
39377 for (unsigned i = 0; i < nnew_nodes; i++)
39378 {
39379 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
39380 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
39381 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
39382 vertex_nodes.insert(vertex_coord); // Global container
39383 local_vertex_nodes.insert(vertex_coord);
39384 }
39385
39386 // Update the flag to indicate whether an unrefinement or
39387 // refinement was applied
39388 update_was_performed = (unrefinement_applied || refinement_applied);
39389
39390#ifdef OOMPH_HAS_MPI
39391 if (this->is_mesh_distributed())
39392 {
39393 // Add the set of vertices for the boundary, this will help to
39394 // detect if we need to deal with sub_boundaries and
39395 // sub_polylines representations
39396 sub_vertex_nodes.push_back(local_vertex_nodes);
39397 // Increase the counter for sub_boundaries
39398 nsub_boundaries++;
39399
39400 // Mark if the polyline created by these vertices will be used
39401 // as a shared boundary or as an internal boundary
39402 if (both_root_face_elements_are_nonhalo)
39403 {
39404 internal_to_shared_boundary.push_back(false);
39405 }
39406 else
39407 {
39408 internal_to_shared_boundary.push_back(true);
39409 }
39410 }
39411#endif
39412
39413 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
39414 // This while is in charge of sorting all the face elements to
39415 // create the new representation of the polyline (also deals
39416 // with the sub-boundary cases)
39417
39418 // Now turn into vector for ease of handling...
39419 const unsigned npoly_vertex = vertex_nodes.size();
39420 tmp_vector_vertex_node.resize(npoly_vertex);
39421 unsigned count = 0;
39422 for (std::set<Vector<double>>::iterator it = vertex_nodes.begin();
39423 it != vertex_nodes.end();
39424 ++it)
39425 {
39426 tmp_vector_vertex_node[count].resize(3);
39427 tmp_vector_vertex_node[count][0] = (*it)[0];
39428 tmp_vector_vertex_node[count][1] = (*it)[1];
39429 tmp_vector_vertex_node[count][2] = (*it)[2];
39430 ++count;
39431 }
39432
39433#ifdef OOMPH_HAS_MPI
39434 // Check that the number of set of vertices marked to be part of a
39435 // shared boundary or of an internal boundaries be the same as the
39436 // total number of sub-boundaries
39437#ifdef PARANOID
39438 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
39439 const unsigned ninternal_to_shared_boundaries =
39440 internal_to_shared_boundary.size();
39441 if (nsub_boundaries_set != ninternal_to_shared_boundaries)
39442 {
39443 std::ostringstream error_message;
39444 error_message
39445 << "The number of found sub-boundaries and the number of marked "
39446 << "internal\nboundaries are different\n"
39447 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39448 << "Number of marked internal boundaries: ("
39449 << ninternal_to_shared_boundaries << ")\n\n";
39450 throw OomphLibError(error_message.str(),
39451 OOMPH_CURRENT_FUNCTION,
39452 OOMPH_EXCEPTION_LOCATION);
39453 }
39454#endif
39455
39456 // --------- Stuff for the sub_boundaries ----- Begin section -------
39457#ifdef PARANOID
39458 if (nsub_boundaries_set != nsub_boundaries)
39459 {
39460 std::ostringstream error_message;
39461 error_message
39462 << "The number of found sub-boundaries and the number of counted\n"
39463 << "sub-boundaries are different:\n"
39464 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
39465 << "Number of counted sub-boundaries: (" << nsub_boundaries
39466 << ")\n\n";
39467 throw OomphLibError(error_message.str(),
39468 OOMPH_CURRENT_FUNCTION,
39469 OOMPH_EXCEPTION_LOCATION);
39470 }
39471#endif
39472
39473 // Verify if need to deal with sub_boundaries
39474 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39475 {
39476 // Mark the boundary as been splitted in the partition process
39477 this->Boundary_was_splitted[bound] = true;
39478
39479 // Resize the vector to store the info. of sub-boundaries
39480 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
39481 // Loop over the sub-boundaries
39482 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39483 {
39484 // Turn info. into vector for ease of handling...
39485 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
39486 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
39487 unsigned subcount = 0;
39488 std::set<Vector<double>>::iterator subit;
39489 for (subit = sub_vertex_nodes[isub].begin();
39490 subit != sub_vertex_nodes[isub].end();
39491 ++subit)
39492 {
39493 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
39494 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
39495 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
39496 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
39497 ++subcount;
39498 }
39499 }
39500 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39501 // --------- Stuff for the sub_boundaries ----- End section ----------
39502#endif // OOMPH_HAS_MPI
39503
39504 // For further processing the three-dimensional vector has to be
39505 // reduced to a two-dimensional vector
39506 unsigned n_vertex = tmp_vector_vertex_node.size();
39507
39508 // Resize the vector for vectices
39509 vector_vertex_node.resize(n_vertex);
39510 for (unsigned i = 0; i < n_vertex; i++)
39511 {
39512 vector_vertex_node[i].resize(2);
39513 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
39514 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
39515 }
39516
39517#ifdef OOMPH_HAS_MPI
39518 // --------- Stuff for the sub_boundaries ----- Begin section -------
39519 // Verify if need to deal with sub_boundaries
39520 if (this->is_mesh_distributed() && nsub_boundaries > 1)
39521 {
39522 // For further processing the three-dimensional vector has to be
39523 // reduced to a two-dimensional vector
39524 // Resize the vector to store the info. of sub-boundaries
39525 sub_vector_vertex_node.resize(nsub_boundaries);
39526 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39527 {
39528 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
39529 // Resize the vector for vectices
39530 sub_vector_vertex_node[isub].resize(subn_vertex);
39531 for (unsigned i = 0; i < subn_vertex; i++)
39532 {
39533 sub_vector_vertex_node[isub][i].resize(2);
39534 sub_vector_vertex_node[isub][i][0] =
39535 sub_tmp_vector_vertex_node[isub][i][1];
39536 sub_vector_vertex_node[isub][i][1] =
39537 sub_tmp_vector_vertex_node[isub][i][2];
39538 }
39539 }
39540 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39541
39542 // We already have the info. for the sub-boundaries (if necessary)
39543 // and then we can create the sub-boundaries representations to
39544 // ease the generation of the mesh by Triangle
39545
39546 // --------- Stuff for the sub_boundaries ----- End section ---------
39547#endif // OOMPH_HAS_MPI
39548
39549 // ------------------------------------------------------------------
39550 // Check for contiguousness
39551 // ------------------------------------------------------------------
39552#ifdef OOMPH_HAS_MPI
39553 // Only perform this checking if the mesh is not distributed When
39554 // the mesh is distributed the polylines continuity is addressed by
39555 // the sort_polylines_helper() method
39556 if (!this->is_mesh_distributed())
39557#endif
39558 {
39559 if (cs > 0)
39560 {
39561 // Final end point of previous line
39562 Vector<double> final_vertex_of_previous_segment;
39563 unsigned n_prev_vertex =
39564 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
39565 final_vertex_of_previous_segment =
39566 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
39567 n_prev_vertex - 1);
39568
39569 unsigned prev_seg_boundary_id =
39570 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39571
39572 // Find the error between the final vertex of the previous
39573 // line and the first vertex of the current line
39574 double error = 0.0;
39575 for (unsigned i = 0; i < 2; i++)
39576 {
39577 const double dist = final_vertex_of_previous_segment[i] -
39578 (*vector_vertex_node.begin())[i];
39579 error += dist * dist;
39580 }
39581 error = sqrt(error);
39582
39583 // If the error is bigger than the tolerance then
39584 // we probably need to reverse, but better check
39586 {
39587 // Find the error between the final vertex of the previous
39588 // line and the last vertex of the current line
39589 double rev_error = 0.0;
39590 for (unsigned i = 0; i < 2; i++)
39591 {
39592 const double dist = final_vertex_of_previous_segment[i] -
39593 (*--vector_vertex_node.end())[i];
39594 rev_error += dist * dist;
39595 }
39596 rev_error = sqrt(rev_error);
39597
39598 if (rev_error >
39600 {
39601 // It could be possible that the first segment be reversed and we
39602 // did not notice it because this check does not apply for the
39603 // first segment. We can verify if the first segment is reversed
39604 // by using the vertex number 1
39605 if (cs == 1)
39606 {
39607 // Initial end point of previous line
39608 Vector<double> initial_vertex_of_previous_segment;
39609
39610 initial_vertex_of_previous_segment =
39611 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
39612
39613 unsigned prev_seg_boundary_id =
39614 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
39615
39616 // Find the error between the initial vertex of the previous
39617 // line and the first vertex of the current line
39618 double error = 0.0;
39619 for (unsigned i = 0; i < 2; i++)
39620 {
39621 const double dist = initial_vertex_of_previous_segment[i] -
39622 (*vector_vertex_node.begin())[i];
39623 error += dist * dist;
39624 }
39625 error = sqrt(error); // Reversed only the previous one
39626
39627 // If the error is bigger than the tolerance then
39628 // we probably need to reverse, but better check
39629 if (error >
39631 {
39632 // Find the error between the final vertex of the previous
39633 // line and the last vertex of the current line
39634 double rev_error = 0.0;
39635 for (unsigned i = 0; i < 2; i++)
39636 {
39637 const double dist = initial_vertex_of_previous_segment[i] -
39638 (*--vector_vertex_node.end())[i];
39639 rev_error += dist * dist;
39640 }
39641 rev_error = sqrt(rev_error); // Reversed both the current
39642 // one and the previous one
39643
39644 if (rev_error >
39646 {
39647 std::ostringstream error_stream;
39648 error_stream
39649 << "The distance between the first node of the current\n"
39650 << "line segment (boundary " << bound
39651 << ") and either end of "
39652 << "the previous line segment\n"
39653 << "(boundary " << prev_seg_boundary_id
39654 << ") is bigger than"
39655 << " the desired tolerance "
39657 << ".\n"
39658 << "This suggests that the polylines defining the "
39659 "polygonal\n"
39660 << "representation are not properly ordered.\n"
39661 << "Fail on last vertex of polyline: ("
39662 << prev_seg_boundary_id
39663 << ") and\nfirst vertex of polyline (" << bound
39664 << ").\nThis should have failed when first trying to "
39665 << "construct the\npolygon.\n";
39666 throw OomphLibError(error_stream.str(),
39667 OOMPH_CURRENT_FUNCTION,
39668 OOMPH_EXCEPTION_LOCATION);
39669 }
39670 else
39671 {
39672 // Reverse both
39673 // Reverse the current vector to line up with the previous
39674 // one
39675 std::reverse(vector_vertex_node.begin(),
39676 vector_vertex_node.end());
39677 open_curve_pt->polyline_pt(cs - 1)->reverse();
39678 }
39679 }
39680 else
39681 {
39682 // Reverse the previous one
39683 open_curve_pt->polyline_pt(cs - 1)->reverse();
39684 }
39685
39686 } // if (cs == 1)
39687 else
39688 {
39689 std::ostringstream error_stream;
39690 error_stream
39691 << "The distance between the first node of the current\n"
39692 << "line segment (boundary " << bound
39693 << ") and either end of "
39694 << "the previous line segment\n"
39695 << "(boundary " << prev_seg_boundary_id
39696 << ") is bigger than the "
39697 << "desired tolerance "
39699 << ".\n"
39700 << "This suggests that the polylines defining the polygonal\n"
39701 << "representation are not properly ordered.\n"
39702 << "Fail on last vertex of polyline: ("
39703 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
39704 << bound << ").\n"
39705 << "This should have failed when first trying to construct\n"
39706 << "the polygon.\n";
39707 throw OomphLibError(error_stream.str(),
39708 OOMPH_CURRENT_FUNCTION,
39709 OOMPH_EXCEPTION_LOCATION);
39710 }
39711 }
39712 else
39713 {
39714 // Reverse the current vector to line up with the previous one
39715 std::reverse(vector_vertex_node.begin(),
39716 vector_vertex_node.end());
39717 }
39718 }
39719
39720 } // if (cs > 0)
39721
39722 } // if (!this->is_mesh_distributed())
39723
39724 // ---------------------------------------------------------------
39725 // Update the polylines representation
39726 // ---------------------------------------------------------------
39727 // Always update the polylines representation, in a distributed
39728 // mesh it is necessary to update the polyline representation since
39729 // it may no longer have vertices (the boundary may not be part of
39730 // the domain in the current processor)
39731
39732 // The new number of vertices
39733 n_vertex = vector_vertex_node.size();
39734
39735 // Update the polyline according to the new vertices
39736 TriangleMeshPolyLine* tmp_polyline_pt =
39737 new TriangleMeshPolyLine(vector_vertex_node, bound);
39738
39739 // Create a temporal "curve section" version of the recently
39740 // created polyline
39741 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
39742
39743 // Tolerance below which the middle point can be deleted (ratio of
39744 // deflection to element length)
39745 double unrefinement_tolerance =
39746 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
39747
39748 // Tolerance to add points
39749 double refinement_tolerance =
39750 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
39751
39752 // Establish refinement and unrefinement tolerance
39753 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
39754 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
39755
39756 // Establish the maximum length constraint
39757 double maximum_length = open_curve_pt->polyline_pt(cs)->maximum_length();
39758 tmp_polyline_pt->set_maximum_length(maximum_length);
39759
39760#ifdef OOMPH_HAS_MPI
39761 // If the mesh is distributed check that the polyline still has
39762 // vertices
39763 if (this->is_mesh_distributed())
39764 {
39765 if (n_vertex >= 2)
39766 {
39767 // Pass the connection information from the old polyline to
39768 // the new one
39769 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39770 tmp_curve_section_pt);
39771 } // if (n_vertex >= 2)
39772 } // if (this->is_mesh_distributed())
39773 else
39774#endif
39775 {
39776 // Pass the connection information from the old polyline to the
39777 // new one
39778 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
39779 tmp_curve_section_pt);
39780 }
39781
39782 // Now update the polyline according to the new vertices but first
39783 // check if the object is allowed to delete the representation or
39784 // if it should be done by other object
39785 bool delete_it_on_destructor = false;
39786
39787 std::set<TriangleMeshCurveSection*>::iterator it =
39788 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
39789
39790 if (it != this->Free_curve_section_pt.end())
39791 {
39792 this->Free_curve_section_pt.erase(it);
39793 delete open_curve_pt->curve_section_pt(cs);
39794 delete_it_on_destructor = true;
39795 }
39796
39797 // -------------------------------------------------------------
39798 // Copying the new representation
39799 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
39800
39801 // Update the Boundary - Polyline map
39802 this->Boundary_curve_section_pt[bound] =
39803 open_curve_pt->curve_section_pt(cs);
39804
39805 if (delete_it_on_destructor)
39806 {
39807 this->Free_curve_section_pt.insert(open_curve_pt->curve_section_pt(cs));
39808 }
39809
39810#ifdef OOMPH_HAS_MPI
39811 // If there are not sub-boundaries mark the boundary if need to be
39812 // trated as shared or as internal boundary
39813 if (this->is_mesh_distributed() && nsub_boundaries == 1)
39814 {
39815 // Clear all previous stored data
39816 this->Boundary_marked_as_shared_boundary[bound].clear();
39817
39818 // .. and store the flag for the boundary
39819 this->Boundary_marked_as_shared_boundary[bound].push_back(
39820 internal_to_shared_boundary[0]);
39821 }
39822 // --------- Stuff for the sub_boundaries ----- Begin section --------
39823 // Verify if need to deal with sub_boundaries
39824 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
39825 {
39826 // Create temporary representations for the boundaries, only to
39827 // create the mesh when calling Triangle
39828
39829 // Clear all previous stored data
39830 this->Boundary_subpolylines[bound].clear();
39831 // Now create storage for the sub-boundaries
39832 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
39833
39834 // Clear all previous stored data
39835 this->Boundary_marked_as_shared_boundary[bound].clear();
39836 // Create storage to mark the internal boundaries as shared
39837 // boundaries
39838 this->Boundary_marked_as_shared_boundary[bound].resize(nsub_boundaries);
39839 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
39840 {
39841 // Now update the polyline according to the sub set of
39842 // vertices, set the chunk number of the polyline
39843 TriangleMeshPolyLine* sub_tmp_polyline_pt =
39844 new TriangleMeshPolyLine(sub_vector_vertex_node[isub], bound, isub);
39845
39846 // Add the sub-polyline to the container to represent the
39847 // boundary in parts
39848 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
39849
39850 // Copy the flag that mark the boundary as internal or as
39851 // shared bound
39852 this->Boundary_marked_as_shared_boundary[bound][isub] =
39853 internal_to_shared_boundary[isub];
39854
39855 // No need to send the unrefinement/refinement and maximum
39856 // length constraints since these are only temporary
39857 // representations
39858
39859 // But we certanly we need to pass the connection information
39860 // to the sub-polylines
39861 // Get a curve section representation of the sub-polyline
39862 TriangleMeshCurveSection* tmp_sub_curve_section_pt =
39863 sub_tmp_polyline_pt;
39864 this->copy_connection_information_to_sub_polylines(
39865 tmp_curve_section_pt, tmp_sub_curve_section_pt);
39866
39867 } // for (isub < nsub_boundaries)
39868
39869 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
39870 // --------- Stuff for the sub_boundaries ----- End section ---------
39871#endif // OOMPH_HAS_MPI
39872
39873 // Delete the allocated memory for the geometric object
39874 // that represents the curvilinear boundary
39875 delete mesh_geom_obj_pt;
39876
39877 } // for (cs < ncurve_section)
39878
39879 // Cleanup the face mesh
39880 for (unsigned p = 0; p < ncurve_section; p++)
39881 {
39882 face_mesh_pt[p]->flush_node_storage();
39883 delete face_mesh_pt[p];
39884 }
39885
39886 return update_was_performed;
39887 }
39888
39889#ifdef OOMPH_HAS_MPI
39890 //======================================================================
39891 /// Updates the polylines using the elements area as
39892 /// constraint for the number of points along the boundaries
39893 //======================================================================
39894 template<class ELEMENT>
39896 Vector<TriangleMeshPolyLine*>& vector_polyline_pt,
39897 const Vector<double>& target_areas)
39898 {
39899 // Flag to check if there were a change on the shared boundary
39900 // representation
39901 unsigned update_was_performed = false;
39902
39903 // Go through all the shared boundaries/polylines
39904 const unsigned n_polylines = vector_polyline_pt.size();
39905 for (unsigned pp = 0; pp < n_polylines; pp++)
39906 {
39907 // Get the boundary id of the current polyline
39908 const unsigned shd_bnd_id = vector_polyline_pt[pp]->boundary_id();
39909
39910 // Get the chunk number
39911 const unsigned chunk = vector_polyline_pt[pp]->boundary_chunk();
39912
39913 // Get the face elements that created the shared boundary from the
39914 // bulk shared boundary elements
39915
39916 // Compute the face elements from the shared boundary elements,
39917 // create an association from the face element with the "bulk"
39918 // elements
39919 std::map<FiniteElement*, FiniteElement*> face_ele_pt_to_bulk_element_pt;
39920
39921 // The temporary storage for the halo face elements
39922 Vector<FiniteElement*> halo_shared_face_ele_pt;
39923 // The temporary storage for the nonhalo face elements
39924 Vector<FiniteElement*> nonhalo_shared_face_ele_pt;
39925
39926 // Get the number of shared boundary elements associated with the
39927 // current shared boundary
39928 const unsigned nshared_bound_ele =
39929 this->nshared_boundary_element(shd_bnd_id);
39930
39931 // Loop over the elements in the shared boundary to create the face
39932 // elements
39933 for (unsigned e = 0; e < nshared_bound_ele; e++)
39934 {
39935 // Get the shared boundary element
39936 FiniteElement* bulk_ele_pt =
39937 this->shared_boundary_element_pt(shd_bnd_id, e);
39938
39939 // Get the face index
39940 int face_index = this->face_index_at_shared_boundary(shd_bnd_id, e);
39941
39942 // Before adding the new element we need to ensure that the edge
39943 // that this element represents has not been already added
39944 FiniteElement* face_ele_pt =
39945 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
39946
39947 // Establish the association between the bulk element and the
39948 // face element
39949 face_ele_pt_to_bulk_element_pt[face_ele_pt] = bulk_ele_pt;
39950
39951 // Nonhalo element
39952 if (!bulk_ele_pt->is_halo())
39953 {
39954 // Add nonhalo shared face element to the container
39955 nonhalo_shared_face_ele_pt.push_back(face_ele_pt);
39956 }
39957 else // halo element
39958 {
39959 // Add halo shared face element to the container
39960 halo_shared_face_ele_pt.push_back(face_ele_pt);
39961 }
39962
39963 } // for (e < nshared_bound_ele)
39964
39965 // Now we have the face elements, we need to ensure that the halo
39966 // and nonhalo bulk element are sorted one after the other
39967 Vector<Vector<FiniteElement*>> unsorted_shared_bulk_ele_pt;
39968
39969 // Mark the face elements already used
39970 std::map<FiniteElement*, bool> shared_face_done;
39971
39972 // Get the number of nonhalo face elements
39973 const unsigned nnonhalo_face_shared_ele =
39974 nonhalo_shared_face_ele_pt.size();
39975
39976 // Get the number of halo face elements
39977 const unsigned nhalo_face_shared_ele = halo_shared_face_ele_pt.size();
39978
39979#ifdef PARANOID
39980 // The number of nonhalo shared face boundary elements must be the
39981 // half of the total number of shared boundary elements
39982 if (nshared_bound_ele / 2 != nnonhalo_face_shared_ele)
39983 {
39984 std::ostringstream error_message;
39985 error_message
39986 << "The number of shared boundary elements (" << nshared_bound_ele
39987 << ") is not the double\nof the number of unsorted NONHALO shared "
39988 << "face boundary elements (" << nnonhalo_face_shared_ele << ")\n"
39989 << "for the current boundary (" << shd_bnd_id << ")\n\n";
39990 throw OomphLibError(error_message.str(),
39991 OOMPH_CURRENT_FUNCTION,
39992 OOMPH_EXCEPTION_LOCATION);
39993 }
39994
39995 // The number of halo shared face boundary elements must be the
39996 // half of the total number of shared boundary elements
39997 if (nshared_bound_ele / 2 != nhalo_face_shared_ele)
39998 {
39999 std::ostringstream error_message;
40000 error_message
40001 << "The number of shared boundary elements (" << nshared_bound_ele
40002 << ") is not the double\nof the number of unsorted HALO shared "
40003 << "face boundary elements (" << nhalo_face_shared_ele << ")\n"
40004 << "for the current boundary (" << shd_bnd_id << ")\n\n";
40005 throw OomphLibError(error_message.str(),
40006 OOMPH_CURRENT_FUNCTION,
40007 OOMPH_EXCEPTION_LOCATION);
40008 }
40009#endif
40010
40011 // ------------------------------------------------------------------
40012 // Loop over the nonhalo face elements and look for the halo face
40013 // element at the other side of the shared boundary
40014 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40015 {
40016 // Get the inh-th face element
40017 FiniteElement* nonhalo_face_ele_pt = nonhalo_shared_face_ele_pt[inh];
40018
40019 // Get the number of nodes on the face element
40020 const unsigned nnodes_nh = nonhalo_face_ele_pt->nnode();
40021 // Get the first and last node on the element
40022 Node* nh_first_node_pt = nonhalo_face_ele_pt->node_pt(0);
40023 Node* nh_last_node_pt = nonhalo_face_ele_pt->node_pt(nnodes_nh - 1);
40024
40025 // Now find the (halo) face element at the other side of the
40026 // shared boundary
40027 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40028 {
40029 // Get the ih-th face element
40030 FiniteElement* halo_face_ele_pt = halo_shared_face_ele_pt[ih];
40031
40032 // Check that the face element has not been done
40033 if (!shared_face_done[halo_face_ele_pt])
40034 {
40035 // Get the number of nodes on the face element
40036 const unsigned nnodes_h = halo_face_ele_pt->nnode();
40037 // Get the first and last node on the element
40038 Node* h_first_node_pt = halo_face_ele_pt->node_pt(0);
40039 Node* h_last_node_pt = halo_face_ele_pt->node_pt(nnodes_h - 1);
40040
40041 // If the nodes are the same then we have found the (halo)
40042 // face element at the other side of the shared boundary
40043 if (nh_first_node_pt == h_first_node_pt &&
40044 nh_last_node_pt == h_last_node_pt)
40045 {
40046 // Get the BULK elements associated with the face elements
40047 Vector<FiniteElement*> tmp_bulk_element_pt;
40048 // Get the BULK elements associated to the face elements
40049 // (the nonhalo and the halo)
40050 FiniteElement* nonhalo_bulk_ele_pt =
40051 face_ele_pt_to_bulk_element_pt[nonhalo_face_ele_pt];
40052 FiniteElement* halo_bulk_ele_pt =
40053 face_ele_pt_to_bulk_element_pt[halo_face_ele_pt];
40054
40055 // Add the BULK elements to the temporal storage
40056 tmp_bulk_element_pt.push_back(nonhalo_bulk_ele_pt);
40057 tmp_bulk_element_pt.push_back(halo_bulk_ele_pt);
40058
40059 // Store the pair of elements associated to the "edge"
40060 unsorted_shared_bulk_ele_pt.push_back(tmp_bulk_element_pt);
40061
40062 // Mark the face elements as done
40063 shared_face_done[nonhalo_face_ele_pt] = true;
40064 shared_face_done[halo_face_ele_pt] = true;
40065
40066 // Break the loop for (ih < nhalo_face_shared_ele)
40067 break;
40068 } // if (nh_first_node_pt == h_first_node_pt &&
40069 // nh_last_node_pt == h_last_node_pt)
40070 else if (nh_first_node_pt == h_last_node_pt &&
40071 nh_last_node_pt == h_first_node_pt)
40072 {
40073 // Get the BULK elements associated with the face elements
40074 Vector<FiniteElement*> tmp_bulk_element_pt;
40075 // Get the BULK elements associated to the face elements
40076 // (the nonhalo and the halo)
40077 FiniteElement* nonhalo_bulk_ele_pt =
40078 face_ele_pt_to_bulk_element_pt[nonhalo_face_ele_pt];
40079 FiniteElement* halo_bulk_ele_pt =
40080 face_ele_pt_to_bulk_element_pt[halo_face_ele_pt];
40081
40082 // Add the BULK elements to the temporal storage
40083 tmp_bulk_element_pt.push_back(nonhalo_bulk_ele_pt);
40084 tmp_bulk_element_pt.push_back(halo_bulk_ele_pt);
40085
40086 // Store the pair of elements associated to the "edge"
40087 unsorted_shared_bulk_ele_pt.push_back(tmp_bulk_element_pt);
40088
40089 // Mark the face elements as done
40090 shared_face_done[nonhalo_face_ele_pt] = true;
40091 shared_face_done[halo_face_ele_pt] = true;
40092
40093 // Break the loop for (ih < nhalo_face_shared_ele)
40094 break;
40095 } // else if (nh_first_node_pt == h_last_node_pt &&
40096 // nh_last_node_pt == h_first_node_pt)
40097
40098 } // if (face_done[halo_face_ele_pt])
40099
40100 } // for (ih < nhalo_face_shared_ele)
40101
40102 } // for (inh < nnonhalo_face_shared_ele)
40103
40104 // -------------------------------------------------------------
40105 // Now sort the face elements
40106 // -------------------------------------------------------------
40107
40108 // We already have the shared face elements that make the shared
40109 // boundary (and the bulk elements), now sort them to create a
40110 // contiguous boundary
40111
40112#ifdef PARANOID
40113 const unsigned nunsorted_shared_bulk_ele =
40114 unsorted_shared_bulk_ele_pt.size();
40115
40116 // The number of unsorted shared BULK elements MUST be the same
40117 // as the number of shared_boundary elements divided by two
40118 if (nshared_bound_ele / 2 != nunsorted_shared_bulk_ele)
40119 {
40120 std::ostringstream error_message;
40121 error_message
40122 << "The number of shared boundary elements (" << nshared_bound_ele
40123 << ") is not the double\nof the number of unsorted shared bulk "
40124 << "boundary elements (" << nunsorted_shared_bulk_ele << ")\n"
40125 << "for the current boundary (" << shd_bnd_id << ")\n\n";
40126 throw OomphLibError(error_message.str(),
40127 OOMPH_CURRENT_FUNCTION,
40128 OOMPH_EXCEPTION_LOCATION);
40129 }
40130
40131 // The number of done shared face elements MUST be the same as the
40132 // sum of the nonhalo and halo shared boundary face elements
40133 if ((nnonhalo_face_shared_ele + nhalo_face_shared_ele) !=
40134 shared_face_done.size())
40135 {
40136 std::ostringstream error_message;
40137 error_message << "The number of DONE shared boundary face elements ("
40138 << shared_face_done.size()
40139 << ") is not the same\n as the sum of"
40140 << "the nonhalo face shared boundary elements ("
40141 << nnonhalo_face_shared_ele
40142 << ")\nand the halo face shared "
40143 << "boundary elements (" << nhalo_face_shared_ele
40144 << ") for the\n/"
40145 << "current boundary (" << shd_bnd_id << ")\n\n";
40146 throw OomphLibError(error_message.str(),
40147 OOMPH_CURRENT_FUNCTION,
40148 OOMPH_EXCEPTION_LOCATION);
40149 }
40150#endif
40151
40152 // Clear the already done face elements
40153 shared_face_done.clear();
40154
40155 // The number of sorted face elements
40156 unsigned nsorted_face_ele = 0;
40157
40158 // Storing for the sorting nodes extracted from the face
40159 // elements. This are also used to update the polyline
40160 std::list<Node*> sorted_nodes;
40161
40162 // Storing for the sorted shared face elements
40163 std::list<FiniteElement*> sorted_shared_bound_elements_pt;
40164
40165 // Get the root face element
40166 FiniteElement* root_face_ele_pt = nonhalo_shared_face_ele_pt[0];
40167 nsorted_face_ele++;
40168
40169 // Mark face as done
40170 shared_face_done[root_face_ele_pt] = true;
40171
40172 // The initial and final node on the list
40173 const unsigned nnodes_root = root_face_ele_pt->nnode();
40174 Node* first_node_pt = root_face_ele_pt->node_pt(0);
40175 Node* last_node_pt = root_face_ele_pt->node_pt(nnodes_root - 1);
40176
40177 // Push back on the list the new nodes
40178 sorted_nodes.push_back(first_node_pt);
40179 sorted_nodes.push_back(last_node_pt);
40180
40181 // Store the bulk elements of the current face
40182 sorted_shared_bound_elements_pt.push_back(
40183 unsorted_shared_bulk_ele_pt[0][0]);
40184 sorted_shared_bound_elements_pt.push_back(
40185 unsorted_shared_bulk_ele_pt[0][1]);
40186
40187 // Sort the face elements
40188 while (nsorted_face_ele < nnonhalo_face_shared_ele)
40189 {
40190 // Flag to indicate when a node was added
40191 bool node_added = false;
40192
40193 // Start from the next edge since we have already added the
40194 // previous one as the initial face element
40195 for (unsigned iface = 1; iface < nnonhalo_face_shared_ele; iface++)
40196 {
40197 FiniteElement* tmp_shared_face_ele_pt =
40198 nonhalo_shared_face_ele_pt[iface];
40199
40200 // If face has not been sorted
40201 if (!shared_face_done[tmp_shared_face_ele_pt])
40202 {
40203 // Get the number of nodes for the current face element
40204 const unsigned tmp_nnodes = tmp_shared_face_ele_pt->nnode();
40205
40206 // Get each individual node
40207 Node* left_node_pt = tmp_shared_face_ele_pt->node_pt(0);
40208 Node* right_node_pt =
40209 tmp_shared_face_ele_pt->node_pt(tmp_nnodes - 1);
40210
40211 if (left_node_pt == first_node_pt)
40212 {
40213 // Push front the new node
40214 sorted_nodes.push_front(right_node_pt);
40215 first_node_pt = right_node_pt;
40216 node_added = true;
40217
40218 // Store the elements of the current face element
40219 sorted_shared_bound_elements_pt.push_front(
40220 unsorted_shared_bulk_ele_pt[iface][1]);
40221 sorted_shared_bound_elements_pt.push_front(
40222 unsorted_shared_bulk_ele_pt[iface][0]);
40223 }
40224 else if (left_node_pt == last_node_pt)
40225 {
40226 // Push back the new node
40227 sorted_nodes.push_back(right_node_pt);
40228 last_node_pt = right_node_pt;
40229 node_added = true;
40230
40231 // Store the elements of the current face element
40232 sorted_shared_bound_elements_pt.push_back(
40233 unsorted_shared_bulk_ele_pt[iface][0]);
40234 sorted_shared_bound_elements_pt.push_back(
40235 unsorted_shared_bulk_ele_pt[iface][1]);
40236 }
40237 else if (right_node_pt == first_node_pt)
40238 {
40239 // Push front the new node
40240 sorted_nodes.push_front(left_node_pt);
40241 first_node_pt = left_node_pt;
40242 node_added = true;
40243
40244 // Store the elements of the current face element
40245 sorted_shared_bound_elements_pt.push_front(
40246 unsorted_shared_bulk_ele_pt[iface][1]);
40247 sorted_shared_bound_elements_pt.push_front(
40248 unsorted_shared_bulk_ele_pt[iface][0]);
40249 }
40250 else if (right_node_pt == last_node_pt)
40251 {
40252 // Push back the new node
40253 sorted_nodes.push_back(left_node_pt);
40254 last_node_pt = left_node_pt;
40255 node_added = true;
40256
40257 // Store the elements of the current face element
40258 sorted_shared_bound_elements_pt.push_back(
40259 unsorted_shared_bulk_ele_pt[iface][0]);
40260 sorted_shared_bound_elements_pt.push_back(
40261 unsorted_shared_bulk_ele_pt[iface][1]);
40262 }
40263
40264 if (node_added)
40265 {
40266 // Mark as done if one of its nodes has been added to the
40267 // list
40268 shared_face_done[tmp_shared_face_ele_pt] = true;
40269 nsorted_face_ele++;
40270
40271 // Break the for
40272 break;
40273 }
40274
40275 } // if (!shared_face_done[tmp_shared_face_ele_pt])
40276
40277 } // for (iface < nnonhalo_face_shared_ele)
40278
40279 } // while (nsorted_face_ele < nnonhalo_face_shared_ele))
40280
40281 // ----------------------------------------------------------------
40282 // Here we can safely delete the face elements, they are no longer
40283 // required
40284
40285 // First the nonhalo face elements
40286 for (unsigned inh = 0; inh < nnonhalo_face_shared_ele; inh++)
40287 {
40288 delete nonhalo_shared_face_ele_pt[inh];
40289 nonhalo_shared_face_ele_pt[inh] = 0;
40290 } // for (inh < nnonhalo_face_shared_ele)
40291
40292 // ... then the halo face elements
40293 for (unsigned ih = 0; ih < nhalo_face_shared_ele; ih++)
40294 {
40295 delete halo_shared_face_ele_pt[ih];
40296 halo_shared_face_ele_pt[ih] = 0;
40297 } // for (inh < nhalo_face_shared_ele)
40298
40299 // ------------------------------------------------------------------
40300 // At this point we already have a sorted list of nodes, get the
40301 // vertices from them and store them in a vector container
40302
40303 // Get the number of nodes on the list
40304 const unsigned n_nodes = sorted_nodes.size();
40305
40306 // The vector to store the vertices
40307 Vector<Vector<double>> polyline_vertices(n_nodes);
40308
40309 // Copy the vertices from the nodes
40310 unsigned counter = 0;
40311 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
40312 it_nodes != sorted_nodes.end();
40313 it_nodes++)
40314 {
40315 polyline_vertices[counter].resize(2);
40316 polyline_vertices[counter][0] = (*it_nodes)->x(0);
40317 polyline_vertices[counter][1] = (*it_nodes)->x(1);
40318 counter++;
40319 }
40320
40321 // ------------------------------------------------------------------
40322 // Now get the target areas associated to the shared boundary
40323 // elements
40324
40325 // Copy the sorted elements in a vector
40326 Vector<FiniteElement*> sorted_shared_ele_pt;
40327 for (std::list<FiniteElement*>::iterator it_ele =
40328 sorted_shared_bound_elements_pt.begin();
40329 it_ele != sorted_shared_bound_elements_pt.end();
40330 it_ele++)
40331 {
40332 sorted_shared_ele_pt.push_back((*it_ele));
40333 }
40334
40335 // Get the number of target areas
40336 const unsigned n_shared_target_areas = sorted_shared_ele_pt.size();
40337 Vector<double> sorted_shared_target_areas(n_shared_target_areas);
40338
40339 // Mark those shared elements already found
40340 std::map<std::pair<GeneralisedElement*, unsigned>, bool> shared_ele_done;
40341
40342 // Counter for the number of already done shared elements
40343 unsigned count_found_shared_element = 0;
40344
40345 // Get the target area associated to the shared boundary elements
40346 const unsigned nele = this->nelement();
40347
40348 // Loop over the elements to find the target areas associated to
40349 // the shared boundary elements
40350 for (unsigned e = 0; e < nele; e++)
40351 {
40352 GeneralisedElement* current_ele_pt = this->element_pt(e);
40353 // Now compare the current element with those in the sorted
40354 // shared element array
40355 for (unsigned s = 0; s < n_shared_target_areas; s++)
40356 {
40357 // Get the element
40358 GeneralisedElement* current_shared_ele_pt = sorted_shared_ele_pt[s];
40359 // Create the pair element-index to check if done
40360 std::pair<GeneralisedElement*, unsigned> pair_gen_ele_idx =
40361 std::make_pair(current_shared_ele_pt, s);
40362 if (!shared_ele_done[pair_gen_ele_idx])
40363 {
40364 // Compare with the global element
40365 if (current_ele_pt == current_shared_ele_pt)
40366 {
40367 // Store the target area of the current shared element
40368 sorted_shared_target_areas[s] = target_areas[e];
40369 // Mark the shared element as done
40370 shared_ele_done[pair_gen_ele_idx] = true;
40371 // Increase the number of found elements
40372 count_found_shared_element++;
40373 } // if (current_ele_pt == current_shared_ele_pt)
40374 } // if (!shared_ele_done[current_shared_ele_pt])
40375 } // for (s < nshared_taget_areas)
40376
40377 // Check if all shared elements have been found
40378 if (count_found_shared_element == n_shared_target_areas)
40379 {
40380 break;
40381 }
40382
40383 } // for (e < nele)
40384
40385#ifdef PARANOID
40386 // Check if the number of found target areas is the same as the
40387 // number of shared target areas
40388 if (count_found_shared_element != n_shared_target_areas)
40389 {
40390 std::ostringstream error_message;
40391 error_message << "The number of found target areas ("
40392 << count_found_shared_element
40393 << ") is different from the "
40394 << "total number\nof target areas ("
40395 << n_shared_target_areas << ") in shared boundary ("
40396 << shd_bnd_id << ")\n\n";
40397 throw OomphLibError(error_message.str(),
40398 OOMPH_CURRENT_FUNCTION,
40399 OOMPH_EXCEPTION_LOCATION);
40400 }
40401#endif
40402
40403 // The number of vertices
40404 const unsigned n_vertices = n_nodes;
40405
40406 // Get the number of segments from the input vector_polyline_pt
40407 const unsigned n_segments = vector_polyline_pt[pp]->nsegment();
40408 // Get the number of segments from the input vector_polyline_pt to
40409 // ensure that the shared boundary corresponds to the one
40410 // represented by the shared face elements (this has sence when the
40411 // mesh was re-created from re-starting)
40412
40413 // Check that the number of vertices correspond with the number of
40414 // segments
40415#ifdef PARANOID
40416 if (n_segments != n_vertices - 1)
40417 {
40418 std::ostringstream error_message;
40419 error_message
40420 << "The number of segments from the current shared polyline "
40421 << "(" << n_segments << ") does not\ncorrespond with the number of "
40422 << "sorted vertices (" << n_vertices - 1
40423 << ") of the current shared\n"
40424 << "boundary\n\n";
40425 throw OomphLibError(error_message.str(),
40426 OOMPH_CURRENT_FUNCTION,
40427 OOMPH_EXCEPTION_LOCATION);
40428 }
40429
40430 // Check that the number of target areas correspond with the number
40431 // of vertices
40432 if (n_segments != n_shared_target_areas / 2)
40433 {
40434 std::ostringstream error_message;
40435 error_message
40436 << "The number of segments for the current sorting of edges "
40437 << "(" << n_segments << ") is different\nfrom the number of "
40438 << "target areas (" << n_shared_target_areas / 2 << ")\n\n";
40439 throw OomphLibError(error_message.str(),
40440 OOMPH_CURRENT_FUNCTION,
40441 OOMPH_EXCEPTION_LOCATION);
40442 }
40443#endif
40444
40445 // ------------------------------------------------------------------
40446 // Get the target areas that are used to perform the unrefinement
40447 // and refinement operation. For each face element on a shared
40448 // polyline there are two bulk elements, a halo and a haloed
40449 // element, each with an associated target area. Review the
40450 // function
40451 // TriangleMesh::create_polylines_from_halo_elements_helper() to
40452 // check how the shared boundaries were created
40453 Vector<double> polyline_target_area(n_segments);
40454 // Loop over the segments in the shared polyline
40455 for (unsigned s = 0; s < n_segments; s++)
40456 {
40457 // Get the minimum of the associated target areas
40458 polyline_target_area[s] =
40459 std::min(sorted_shared_target_areas[s * 2],
40460 sorted_shared_target_areas[(s * 2) + 1]);
40461 }
40462
40463 // Before going to the unrefinement or refinement process check
40464 // that in all processors where the shared boundary lives start
40465 // from the same vertex.
40466 // Start from the bottom left vertex
40467 if (polyline_vertices[n_vertices - 1][1] < polyline_vertices[0][1])
40468 {
40469 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40470 std::reverse(polyline_target_area.begin(), polyline_target_area.end());
40471 }
40472 else if (polyline_vertices[n_vertices - 1][1] == polyline_vertices[0][1])
40473 {
40474 if (polyline_vertices[n_vertices - 1][0] < polyline_vertices[0][0])
40475 {
40476 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
40477 std::reverse(polyline_target_area.begin(),
40478 polyline_target_area.end());
40479 }
40480 }
40481
40482 // ------------------------------------------------------------------
40483 // Apply unrefinement
40484 bool unrefinement_applied = false;
40485 // Apply unefinement if there are more than three nodes at the
40486 // shared boundary
40487 if (n_vertices > 3)
40488 {
40489 unrefinement_applied =
40490 unrefine_shared_boundary_constrained_by_target_area(
40491 shd_bnd_id, chunk, polyline_vertices, polyline_target_area);
40492 }
40493
40494 // Apply refinement
40495 bool refinement_applied =
40496 refine_shared_boundary_constrained_by_target_area(polyline_vertices,
40497 polyline_target_area);
40498
40499 // Was unrefinement/refinement applied
40500 update_was_performed |= (unrefinement_applied || refinement_applied);
40501
40502 // ------------------------------------------------------------------
40503 // Update the polyline representation of the shared boundary
40504
40505 // The new shared polyline representation
40506 TriangleMeshPolyLine* new_polyline_pt =
40507 new TriangleMeshPolyLine(polyline_vertices, shd_bnd_id);
40508
40509 // Get the curve section representation
40510 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
40511
40512 // Copy the connection information from the old shared polyline to
40513 // the new one
40514 this->copy_connection_information(curve_section_pt, new_polyline_pt);
40515
40516 // Now update the polyline according to the new vertices but first
40517 // check if the object is allowed to delete the representation or
40518 // if it should be done by other object
40519 bool delete_it_on_destructor = false;
40520
40521 // Establish the element as being deleted by the destructor of the
40522 // class
40523 std::set<TriangleMeshCurveSection*>::iterator it =
40524 this->Free_curve_section_pt.find(curve_section_pt);
40525
40526 if (it != this->Free_curve_section_pt.end())
40527 {
40528 this->Free_curve_section_pt.erase(it);
40529 delete curve_section_pt;
40530 delete_it_on_destructor = true;
40531 }
40532
40533 // Copy the new representation to the output vector_polyline_pt
40534 vector_polyline_pt[pp] = new_polyline_pt;
40535
40536 // Get the new curve section representation
40537 TriangleMeshCurveSection* new_curve_section_pt = vector_polyline_pt[pp];
40538
40539 // Update the Boundary - Polyline map
40540 this->Boundary_curve_section_pt[shd_bnd_id] = new_curve_section_pt;
40541
40542 if (delete_it_on_destructor)
40543 {
40544 this->Free_curve_section_pt.insert(new_curve_section_pt);
40545 }
40546
40547 } // for (pp < npoly)
40548
40549 return update_was_performed;
40550 }
40551#endif // #ifdef OOMPH_HAS_MPI
40552
40553 //=========================================================================
40554 /// Helper function that performs the unrefinement process
40555 /// on the specified boundary by using the provided vertices
40556 /// representation and the associated target area.
40557 //=========================================================================
40558 template<class ELEMENT>
40561 const unsigned& b,
40562 const unsigned& c,
40563 Vector<Vector<double>>& vector_bnd_vertices,
40564 double& unrefinement_tolerance,
40565 Vector<double>& area_constraint)
40566 {
40567 // Store the vertices not allowed for deletion
40568 std::set<Vector<double>> no_delete_vertex;
40569
40570 // Does the boundary receives connections?
40571 const bool boundary_receive_connections =
40572 this->boundary_connections(b, c, no_delete_vertex);
40573
40574 // Boolean that indicates whether an actual update of the vertex
40575 // coordinates was performed
40576 bool unrefinement_applied = false;
40577
40578 // Return inmedately
40579 if (!Do_boundary_unrefinement_constrained_by_target_areas)
40580 {
40581 return unrefinement_applied;
40582 }
40583
40584 // Strategy to delete nodes: Consider the target area of the
40585 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40586 // if the number of segments to be added is equal to zero for both
40587 // elements then compute the average of both target areas and check
40588 // if the number of segments is still zero, if that holds mark the
40589 // node to be deleted. Before delete the node check whether it is in
40590 // the non_delete_vertex list. Skip the i+1-th node and go for the
40591 // (i+2)-th one, it means, increase the counter for current node by
40592 // two.
40593
40594 // Number of vertices on the boundary
40595 unsigned n_vertex = vector_bnd_vertices.size();
40596
40597 // Compute a constant value
40598 const double constant_value = 4.0 / sqrt(3.0);
40599
40600 if (n_vertex > 2)
40601 {
40602 // Go through all the vertices and delete points when the target area
40603 // indicates zero points along the boundary
40604 for (unsigned i = 1; i < n_vertex - 1; i += 2)
40605 {
40606 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
40607 {
40608 const double local_zeta_first = vector_bnd_vertices[i - 1][0];
40609 const double local_zeta_last = vector_bnd_vertices[i + 1][0];
40610 const double local_length_zeta =
40611 std::fabs(local_zeta_last - local_zeta_first);
40612
40613 const double x1 = vector_bnd_vertices[i - 1][1];
40614 const double y1 = vector_bnd_vertices[i - 1][2];
40615 const double x2 = vector_bnd_vertices[i + 1][1];
40616 const double y2 = vector_bnd_vertices[i + 1][2];
40617 const double local_length =
40618 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40619
40620 const double x_m = vector_bnd_vertices[i][1];
40621 const double y_m = vector_bnd_vertices[i][2];
40622
40623 const double average_area_constraint =
40624 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40625
40626 // Compute the length of the the side of an equilateral
40627 // triangle
40628 const double length_side =
40629 sqrt(constant_value * average_area_constraint);
40630
40631 const double length_side_zeta =
40632 (local_length_zeta * length_side) / local_length;
40633
40634 // Is the new length greater that the old one
40635 if ((length_side_zeta / local_length_zeta) > 1.0)
40636 {
40637 // If the number of segments is zero then verify the condition for
40638 // deletion of nodes but using the condition in the default
40639 // unrefine_boundary() method. If both conditions are true then
40640 // delete the node
40641 // Maths from
40642 // http://www.cgafaq.info/wiki/Circle_Through_Three_Points
40643 double a_x = vector_bnd_vertices[i - 1][1];
40644 double a_y = vector_bnd_vertices[i - 1][2];
40645 double b_x = vector_bnd_vertices[i][1];
40646 double b_y = vector_bnd_vertices[i][2];
40647 double c_x = vector_bnd_vertices[i + 1][1];
40648 double c_y = vector_bnd_vertices[i + 1][2];
40649
40650 double a = b_x - a_x;
40651 double b = b_y - a_y;
40652 double c = c_x - a_x;
40653 double d = c_y - a_y;
40654
40655 double e = a * (a_x + b_x) + b * (a_y + b_y);
40656 double f = c * (a_x + c_x) + d * (a_y + c_y);
40657
40658 double g = 2.0 * (a * (c_y - b_y) - b * (c_x - b_x));
40659
40660 bool do_it = false;
40661 if (std::fabs(g) < 1.0e-14)
40662 {
40663 do_it = true;
40664 }
40665 else
40666 {
40667 double p_x = (d * e - b * f) / g;
40668 double p_y = (a * f - c * e) / g;
40669
40670 double r = sqrt(pow((a_x - p_x), 2) + pow((a_y - p_y), 2));
40671
40672 double rhalfca_x = 0.5 * (a_x - c_x);
40673 double rhalfca_y = 0.5 * (a_y - c_y);
40674
40675 double halfca_squared = pow(rhalfca_x, 2) + pow(rhalfca_y, 2);
40676
40677 double sticky_out_bit =
40678 r - sqrt(std::fabs((r * r) - halfca_squared));
40679
40680 // If sticky out bit divided by distance between end nodes
40681 // is less than tolerance the boundary is so flat that we
40682 // can safely kill the node
40683 if ((sticky_out_bit / (2.0 * sqrt(halfca_squared))) <
40684 unrefinement_tolerance)
40685 {
40686 do_it = true;
40687 }
40688 }
40689
40690 // If the vertex was proposed for deletion check if it is
40691 // allowed for being deleted
40692 if (do_it && boundary_receive_connections)
40693 {
40694 // Is the vertex one of the non deletable vertices
40695 for (std::set<Vector<double>>::iterator it =
40696 no_delete_vertex.begin();
40697 it != no_delete_vertex.end();
40698 it++)
40699 {
40700 // Compute the distance between the proposed node to
40701 // delete and the ones that should not be deleted
40702 const double x = (*it)[0];
40703 const double y = (*it)[1];
40704 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
40705 error = sqrt(error);
40706
40707 if (error <
40709 {
40710 // Do not delete the vertex
40711 do_it = false;
40712 break;
40713 }
40714 }
40715
40716 } // if (do_it && boundary_receive_connections)
40717
40718 // Remove node?
40719 if (do_it)
40720 {
40721 vector_bnd_vertices[i].resize(0);
40722 }
40723 } // if (n_seg == 0)
40724 } // if (area_constraint[i] >= 0)
40725 } // for (i < n_vertex-1)
40726
40727 // Create a new (temporary) vector for the nodes, so that deleted nodes
40728 // are not stored
40729 Vector<Vector<double>> compact_vector;
40730
40731 // Compact vector for target areas too
40732 Vector<double> compact_area_constraint;
40733
40734 // Copy only the non deleted nodes
40735 for (unsigned i = 0; i < n_vertex; i++)
40736 {
40737 // If the entry was not deleted include it in the new vector
40738 if (vector_bnd_vertices[i].size() != 0)
40739 {
40740 compact_vector.push_back(vector_bnd_vertices[i]);
40741 }
40742 }
40743
40744 // ------------------------------------------------------------------
40745 // Size of the target areas vector
40746 unsigned nsize_target = area_constraint.size();
40747 if (nsize_target == 1)
40748 {
40749 // No node was deleted, just copy the target area
40750 compact_area_constraint.push_back(area_constraint[0]);
40751 }
40752
40753 // Copy the target areas
40754 for (unsigned i = 1; i < n_vertex; i += 2)
40755 {
40756 // If the entry was not deleted include the target areas of both
40757 // elements sharing the node
40758 if (vector_bnd_vertices[i].size() != 0)
40759 {
40760 compact_area_constraint.push_back(area_constraint[i - 1]);
40761 // To catch the case when working with even number of vertex
40762 if (i < nsize_target)
40763 {
40764 compact_area_constraint.push_back(area_constraint[i]);
40765 }
40766 }
40767 else
40768 {
40769 // If the node was deleted then compute the new target area as the
40770 // average of the target area of the elements sharing the node
40771 double new_area_constraint =
40772 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
40773 compact_area_constraint.push_back(new_area_constraint);
40774 }
40775 }
40776
40777 // If the size of the compact vector is different from the size of the
40778 // vector before applying the area length constraint then the polyline
40779 // was updated
40780 if (n_vertex != compact_vector.size())
40781 {
40782 unrefinement_applied = true;
40783 }
40784
40785 // Copy back to the original vector
40786 n_vertex = compact_vector.size();
40787 vector_bnd_vertices.resize(n_vertex);
40788 for (unsigned i = 0; i < n_vertex; i++)
40789 {
40790 vector_bnd_vertices[i].resize(3);
40791 vector_bnd_vertices[i][0] = compact_vector[i][0];
40792 vector_bnd_vertices[i][1] = compact_vector[i][1];
40793 vector_bnd_vertices[i][2] = compact_vector[i][2];
40794 }
40795
40796 // Copy back to the original vector of target areas
40797 unsigned ntarget_areas = compact_area_constraint.size();
40798 area_constraint.resize(ntarget_areas);
40799 for (unsigned i = 0; i < ntarget_areas; i++)
40800 {
40801 area_constraint[i] = compact_area_constraint[i];
40802 }
40803
40804 } // if (n_vertex > 2)
40805
40806 return unrefinement_applied;
40807 }
40808
40809 //=========================================================================
40810 /// Helper function that performs the refinement process
40811 /// on the specified boundary by using the provided vertices
40812 /// representation and the associated elements target area.
40813 //=========================================================================
40814 template<class ELEMENT>
40817 MeshAsGeomObject* mesh_geom_obj_pt,
40818 Vector<Vector<double>>& vector_bnd_vertices,
40819 double& refinement_tolerance,
40820 Vector<double>& area_constraint)
40821 {
40822 // Boolean that indicates whether an actual update of the vertex
40823 // coordinates was performed
40824 bool refinement_applied = false;
40825
40826 // Return inmedately
40827 if (!Do_boundary_refinement_constrained_by_target_areas)
40828 {
40829 return refinement_applied;
40830 }
40831
40832 // Get the total number of current vertices
40833 unsigned n_vertex = vector_bnd_vertices.size();
40834
40835 // Compute a constant value
40836 const double constant_value = 4.0 / sqrt(3.0);
40837
40838 if (n_vertex > 1)
40839 {
40840 // Create a new (temporary) vector for the nodes, so that new
40841 // nodes can be stored
40842 Vector<Vector<double>> new_vector;
40843
40844 // Go through all the vertices and create points according to the
40845 // specified element area
40846 for (unsigned i = 0; i < n_vertex - 1; i++)
40847 {
40848 // Include the first node
40849 new_vector.push_back(vector_bnd_vertices[i]);
40850
40851 if (area_constraint[i] > 0)
40852 {
40853 double local_zeta_first = vector_bnd_vertices[i][0];
40854 double local_zeta_last = vector_bnd_vertices[i + 1][0];
40855 const double local_length_zeta =
40856 std::fabs(local_zeta_last - local_zeta_first);
40857
40858 // Check if need to interchange the zeta first and the zeta
40859 // last (to ensure the same order in zeta values in any two
40860 // processors)
40861 if (local_zeta_first > local_zeta_last)
40862 {
40863 const double tmp_zeta = local_zeta_first;
40864 local_zeta_first = local_zeta_last;
40865 local_zeta_last = tmp_zeta;
40866 }
40867
40868 const double x1 = vector_bnd_vertices[i][1];
40869 const double y1 = vector_bnd_vertices[i][2];
40870 const double x2 = vector_bnd_vertices[i + 1][1];
40871 const double y2 = vector_bnd_vertices[i + 1][2];
40872 const double local_length =
40873 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
40874
40875 // Compute the length in zeta units
40876 const double length_side = sqrt(constant_value * area_constraint[i]);
40877 const double length_side_zeta =
40878 (local_length_zeta * length_side) / local_length;
40879
40880 // How many segments should be introduced
40881 const double n_seg_double = length_side_zeta / local_length_zeta;
40882
40883 // One segment initialy (the original one)
40884 unsigned n_seg = 1;
40885
40886 // How many more segments to introduce?
40887 n_seg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
40888
40889 // Are there segments to introduce? There must be at least one
40890 // segment, the original one
40891 if (n_seg > 0)
40892 {
40893 // The zeta increment
40894 double zeta_increment = (local_length_zeta) / ((double)n_seg);
40895
40896 Vector<double> zeta(1);
40897 // Create the n_seg segmets between each pair of nodes
40898 for (unsigned s = 1; s < n_seg; s++)
40899 {
40900 // Get the coordinates
40901 zeta[0] = local_zeta_first + zeta_increment * double(s);
40902 Vector<double> vertex(2);
40903 mesh_geom_obj_pt->position(zeta, vertex);
40904
40905 // Create the new node
40906 Vector<double> new_node(3);
40907 new_node[0] = zeta[0];
40908 new_node[1] = vertex[0];
40909 new_node[2] = vertex[1];
40910
40911 // Include the new node
40912 new_vector.push_back(new_node);
40913
40914 } // for (s<=n_seg)
40915
40916 } // if (n_seg > 0)
40917
40918 } // if (area_constraint[i] >= 0)
40919
40920 } // for (i < n_vertex-1)
40921
40922 // Once finished all the vertices add the last node to the vector
40923 new_vector.push_back(vector_bnd_vertices[n_vertex - 1]);
40924
40925 // If the new size of the vector (including the added nodes) is
40926 // different from the size of the vector before applying the
40927 // area length constraint then the polyline was updated
40928 n_vertex = new_vector.size();
40929 if (n_vertex != vector_bnd_vertices.size())
40930 {
40931 refinement_applied = true;
40932 }
40933
40934 // Copy the new representation
40935 vector_bnd_vertices.resize(n_vertex);
40936 for (unsigned i = 0; i < n_vertex; i++)
40937 {
40938 vector_bnd_vertices[i].resize(3);
40939 vector_bnd_vertices[i][0] = new_vector[i][0];
40940 vector_bnd_vertices[i][1] = new_vector[i][1];
40941 vector_bnd_vertices[i][2] = new_vector[i][2];
40942 }
40943
40944 } // if (n_vertex > 1)
40945
40946 return refinement_applied;
40947 }
40948
40949 //======================================================================
40950 /// Helper function that performs the unrefinement process
40951 /// on the specified boundary by using the provided vertices
40952 /// representation and the associated target area.
40953 /// NOTE: This is the version that applies unrefinement to shared
40954 /// boundaries
40955 //======================================================================
40956 template<class ELEMENT>
40959 const unsigned& b,
40960 const unsigned& c,
40961 Vector<Vector<double>>& vector_bnd_vertices,
40962 Vector<double>& area_constraint)
40963 {
40964 // Store the vertices not allowed for deletion
40965 std::set<Vector<double>> no_delete_vertex;
40966
40967 // Does the boundary receives connections?
40968 const bool boundary_receive_connections =
40969 this->boundary_connections(b, c, no_delete_vertex);
40970
40971 // Boolean that indicates whether an actual update of the vertex
40972 // coordinates was performed
40973 bool unrefinement_applied = false;
40974
40975 // Return inmedately
40976 if (!Do_shared_boundary_unrefinement_constrained_by_target_areas)
40977 {
40978 return unrefinement_applied;
40979 }
40980
40981 // Strategy to delete nodes:
40982
40983 // Strategy to delete nodes: Consider the target area of the
40984 // elements (e_i and e_(i+1)) sharing the i-th node (middle node),
40985 // if the number of segments to be added is equal to zero for both
40986 // elements then compute the average of both target areas and check
40987 // if the number of segments is still zero, if that holds mark the
40988 // node to be deleted. Before delete the node check whether it is in
40989 // the non_delete_vertex list. Skip the i+1-th node and go for the
40990 // (i+2)-th one, it means, increase the counter for current node by
40991 // two.
40992
40993 // Number of vertices on the boundary
40994 unsigned n_vertex = vector_bnd_vertices.size();
40995
40996 // Compute a constant value
40997 const double constant_value = 4.0 / sqrt(3.0);
40998
40999 if (n_vertex > 2)
41000 {
41001 // Go through all the vertices and delete points when the target
41002 // area indicates zero points along the boundary
41003 for (unsigned i = 1; i < n_vertex - 1; i += 2)
41004 {
41005 // Is a target area assigned to the left and right element of
41006 // the i-th node
41007 if (area_constraint[i - 1] > 0 && area_constraint[i] > 0)
41008 {
41009 // Get the vertices to the left
41010 const double x1 = vector_bnd_vertices[i - 1][0];
41011 const double y1 = vector_bnd_vertices[i - 1][1];
41012 // ... and to the right of the i-th vertex
41013 const double x2 = vector_bnd_vertices[i + 1][0];
41014 const double y2 = vector_bnd_vertices[i + 1][1];
41015
41016 // The distance
41017 const double local_length =
41018 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41019
41020 // Get the middle vertex
41021 const double x_m = vector_bnd_vertices[i][0];
41022 const double y_m = vector_bnd_vertices[i][1];
41023
41024 // The average area
41025 const double average_area_constraint =
41026 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41027
41028 // Compute the base length of the triangle with
41029 // area_constraint area
41030 const double length_side =
41031 sqrt(constant_value * average_area_constraint);
41032
41033 // Is the new length greater than the old one
41034 if ((length_side / local_length) > 1.0)
41035 {
41036 bool do_it = true;
41037
41038 // If the vertex was proposed for deletion check that it is
41039 // allowed for being deleted
41040 if (do_it && boundary_receive_connections)
41041 {
41042 // Is the vertex one of the non deletable vertices
41043 for (std::set<Vector<double>>::iterator it =
41044 no_delete_vertex.begin();
41045 it != no_delete_vertex.end();
41046 it++)
41047 {
41048 // Compute the distance between the proposed node to delete
41049 // and the ones that should not be deleted
41050 const double x = (*it)[0];
41051 const double y = (*it)[1];
41052 double error = (x_m - x) * (x_m - x) + (y_m - y) * (y_m - y);
41053 error = sqrt(error);
41054
41055 if (error <
41057 {
41058 // Do not delete the vertex
41059 do_it = false;
41060 break;
41061 }
41062 }
41063
41064 } // if (do_it && boundary_receive_connections)
41065
41066 // Remove node?
41067 if (do_it)
41068 {
41069 vector_bnd_vertices[i].resize(0);
41070 }
41071 } // if ((local_length / length_side) <= 1.3)
41072
41073 } // if (area_constraint[i] >= 0)
41074
41075 } // for (i < n_vertex-1)
41076
41077 // Create a new (temporary) vector for the nodes, so that deleted nodes
41078 // are not stored
41079 Vector<Vector<double>> compact_vector;
41080
41081 // Compact vector for target areas too
41082 Vector<double> compact_area_constraint;
41083
41084 // Copy only the non deleted nodes
41085 for (unsigned i = 0; i < n_vertex; i++)
41086 {
41087 // If the entry was not deleted include it in the new vector
41088 if (vector_bnd_vertices[i].size() != 0)
41089 {
41090 compact_vector.push_back(vector_bnd_vertices[i]);
41091 }
41092 }
41093
41094 // ------------------------------------------------------------------
41095 // The number of target areas
41096 unsigned n_area_constraint = area_constraint.size();
41097 if (n_area_constraint == 1)
41098 {
41099 // No node could be deleted then just copy the target area
41100 compact_area_constraint.push_back(area_constraint[0]);
41101 }
41102
41103 // Copy the target areas
41104 for (unsigned i = 1; i < n_vertex; i += 2)
41105 {
41106 // If the entry was not deleted include the target areas of both
41107 // elements sharing the node
41108 if (vector_bnd_vertices[i].size() != 0)
41109 {
41110 compact_area_constraint.push_back(area_constraint[i - 1]);
41111 // To catch the case when working with even number of vertices
41112 if (i < n_area_constraint)
41113 {
41114 compact_area_constraint.push_back(area_constraint[i]);
41115 }
41116 }
41117 else
41118 {
41119 // If the node was deleted then compute the new target area as the
41120 // average of the target area of the elements sharing the node
41121 const double new_area_constraint =
41122 (area_constraint[i - 1] + area_constraint[i]) / 2.0;
41123 compact_area_constraint.push_back(new_area_constraint);
41124 }
41125 } // for (i < n_vertex)
41126
41127 // If the size of the compact vector is different from the size of
41128 // the vector before applying the area length constraint then the
41129 // polyline was updated
41130 if (n_vertex != compact_vector.size())
41131 {
41132 unrefinement_applied = true;
41133 }
41134
41135 // Copy back to the original vector
41136 n_vertex = compact_vector.size();
41137 vector_bnd_vertices.resize(n_vertex);
41138 for (unsigned i = 0; i < n_vertex; i++)
41139 {
41140 vector_bnd_vertices[i].resize(2);
41141 vector_bnd_vertices[i][0] = compact_vector[i][0];
41142 vector_bnd_vertices[i][1] = compact_vector[i][1];
41143 }
41144
41145 // Copy back to the original vector of target areas
41146 unsigned ntarget_areas = compact_area_constraint.size();
41147 area_constraint.resize(ntarget_areas);
41148 for (unsigned i = 0; i < ntarget_areas; i++)
41149 {
41150 area_constraint[i] = compact_area_constraint[i];
41151 }
41152
41153 } // if (n_vertex > 2)
41154
41155 return unrefinement_applied;
41156 }
41157
41158 //======================================================================
41159 /// Helper function that performs the refinement process
41160 /// on the specified boundary by using the provided vertices
41161 /// representation and the associated elements target area.
41162 /// NOTE: This is the version that applies refinement to shared
41163 /// boundaries
41164 //======================================================================
41165 template<class ELEMENT>
41168 Vector<Vector<double>>& vector_bnd_vertices,
41169 Vector<double>& area_constraint)
41170 {
41171 // Boolean that indicates whether an actual update of the vertex
41172 // coordinates was performed
41173 bool refinement_applied = false;
41174
41175 // Return inmedately
41176 if (!Do_shared_boundary_refinement_constrained_by_target_areas)
41177 {
41178 return refinement_applied;
41179 }
41180
41181 // Get the number of segments
41182 unsigned nsegments = vector_bnd_vertices.size() - 1;
41183
41184 // Create a new (temporary) vector for the nodes, so that new nodes
41185 // can be stored
41186 Vector<Vector<double>> tmp_bnd_vertices;
41187
41188 // Compute a constant value
41189 const double constant_value = 4.0 / sqrt(3.0);
41190
41191 for (unsigned s = 0; s < nsegments; s++)
41192 {
41193 Vector<double> left_vertex = vector_bnd_vertices[s];
41194 Vector<double> right_vertex = vector_bnd_vertices[s + 1];
41195
41196 // Initial and final point of the segment
41197 const double x1 = left_vertex[0];
41198 const double y1 = left_vertex[1];
41199 const double x2 = right_vertex[0];
41200 const double y2 = right_vertex[1];
41201
41202 // Lenght of the segment
41203 const double segment_length =
41204 sqrt(((x1 - x2) * (x1 - x2)) + ((y1 - y2) * (y1 - y2)));
41205
41206 // Compute the distance for the new segments
41207 const double new_segment_length =
41208 sqrt(constant_value * area_constraint[s]);
41209
41210 // How many segments should be introduced
41211 const double n_seg_double = new_segment_length / segment_length;
41212
41213 // One segment initialy (the original one)
41214 unsigned nseg = 1;
41215 // How many more segments to introduce?
41216 nseg += static_cast<unsigned>(std::floor(1.0 / n_seg_double));
41217
41218 // The left vertex must be always included, even though no new vertex
41219 // be added
41220 tmp_bnd_vertices.push_back(left_vertex);
41221
41222 // Are there segments to introduce? There must be at least one
41223 // segment, the original one
41224 if (nseg > 0)
41225 {
41226 // Create intermediate vertices
41227 double incrementx = (right_vertex[0] - left_vertex[0]) / (double)(nseg);
41228 double incrementy = (right_vertex[1] - left_vertex[1]) / (double)(nseg);
41229 for (unsigned i = 1; i < nseg; i++)
41230 {
41231 Vector<double> tmp_vertex(2);
41232 tmp_vertex[0] = left_vertex[0] + incrementx * i;
41233 tmp_vertex[1] = left_vertex[1] + incrementy * i;
41234 tmp_bnd_vertices.push_back(tmp_vertex);
41235 } // for (i < nseg)
41236
41237 } // if (nseg > 0)
41238
41239 } // for (s < nsegments)
41240
41241 // Add the last vertex
41242 tmp_bnd_vertices.push_back(vector_bnd_vertices[nsegments]);
41243
41244 // If the new size of the vector (including the added nodes) is
41245 // different from the size of the vector before applying the
41246 // refinement then the polyline was updated
41247 nsegments = tmp_bnd_vertices.size() - 1;
41248 if (nsegments != vector_bnd_vertices.size() - 1)
41249 {
41250 refinement_applied = true;
41251
41252 // Copy across
41253 vector_bnd_vertices.resize(nsegments + 1);
41254 for (unsigned i = 0; i < nsegments + 1; i++)
41255 {
41256 vector_bnd_vertices[i].resize(2);
41257 vector_bnd_vertices[i][0] = tmp_bnd_vertices[i][0];
41258 vector_bnd_vertices[i][1] = tmp_bnd_vertices[i][1];
41259 }
41260 }
41261
41262 return refinement_applied;
41263 }
41264
41265 //======================================================================
41266 /// Updates the polylines representation after restart
41267 //======================================================================
41268 template<class ELEMENT>
41270 TriangleMeshPolygon*& polygon_pt)
41271 {
41272 // **********************************************************************
41273 // 1) Collect the elements adjacet to the polyline boundary id and
41274 // update the polyline
41275 // **********************************************************************
41276
41277 // (1.1) Get the face mesh representation
41278 Vector<Mesh*> face_mesh_pt;
41279 get_face_mesh_representation(polygon_pt, face_mesh_pt);
41280
41281 // (1.2) Create vertices of the polylines by using the vertices of the
41282 // FaceElements
41283 Vector<double> vertex_coord(3); // zeta,x,y
41284 Vector<double> bound_left(1);
41285 Vector<double> bound_right(1);
41286
41287 const unsigned n_polyline = polygon_pt->npolyline();
41288
41289 // Go for each polyline
41290 for (unsigned p = 0; p < n_polyline; p++)
41291 {
41292 // Get the MeshAsGeomObject representation just once per polyline,
41293 // this object is only used by the
41294 // refine_boundary_constrained_by_target_area() method. We get it here
41295 // to ensure that all processors (in a distributed context) get this
41296 // representation just once, and because an AllToAll MPI communication
41297 // is used in this calling
41298 MeshAsGeomObject* mesh_geom_obj_pt =
41299 new MeshAsGeomObject(face_mesh_pt[p]);
41300
41301 // Set of coordinates that are on the boundary
41302 // Set entries are ordered on first entry in vector which stores
41303 // the boundary coordinate so the vertices come out in order!
41304 std::set<Vector<double>> vertex_nodes;
41305
41306 // Vector to store the vertices, transfer the sorted vertices from the
41307 // set to this vector, --- including the z-value ---
41308 Vector<Vector<double>> tmp_vector_vertex_node;
41309
41310 // Vector to store the coordinates of the polylines, same as the
41311 // tmp_vector_vertex_node vector (after adding more nodes) but
41312 // --- without the z-value ---, used to re-generate the polylines
41313 Vector<Vector<double>> vector_vertex_node;
41314
41315#ifdef OOMPH_HAS_MPI
41316 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
41317 // Set of coordinates that are on the boundary (splitted boundary version)
41318 // The first vector is used to allocate the points for each sub-boundary
41319 // Set entries are ordered on first entry in vector which stores
41320 // the boundary coordinate so the vertices come out in order!
41321 Vector<std::set<Vector<double>>> sub_vertex_nodes;
41322
41323 // Vector to store the vertices, transfer the sorted vertices from the
41324 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
41325 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
41326
41327 // Vector to store the coordinates of the polylines that will represent
41328 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
41329 // but --- without the z-value ---, used to generate the sub-polylines
41330 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
41331 // --------- Stuff to deal with splitted boundaries ----------- End ------
41332#endif
41333
41334 // Get the boundary id
41335 unsigned bound = polygon_pt->curve_section_pt(p)->boundary_id();
41336
41337 /// Use a vector of vector for vertices and target areas to
41338 /// deal with the cases when the boundaries are split by the
41339 /// distribution process
41340
41341 // Loop over the face elements (ordered) and add their vertices
41342 const unsigned nface_element = face_mesh_pt[p]->nelement();
41343
41344 // Store the non halo face elements, the ones from which we will
41345 // get the vertices
41346 Vector<FiniteElement*> non_halo_face_element_pt;
41347 // Map to store the index of the face element on a boundary
41348 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
41349
41350 for (unsigned ef = 0; ef < nface_element; ++ef)
41351 {
41352 FiniteElement* ele_face_pt = face_mesh_pt[p]->finite_element_pt(ef);
41353 // Skip the halo elements
41354#ifdef OOMPH_HAS_MPI
41355 if (this->is_mesh_distributed())
41356 {
41357 // Only work with non-halo elements
41358 if (ele_face_pt->is_halo())
41359 {
41360 continue;
41361 }
41362 }
41363#endif
41364 // Add the face element to the vector
41365 non_halo_face_element_pt.push_back(ele_face_pt);
41366 face_element_index_on_boundary[ele_face_pt] = ef;
41367 }
41368
41369 // Get the number of non halo face element
41370 const unsigned nnon_halo_face_element = non_halo_face_element_pt.size();
41371
41372 // Map to know the already sorted face elements
41373 std::map<FiniteElement*, bool> face_element_done;
41374
41375 // Number of done face elements
41376 unsigned nsorted_face_elements = 0;
41377
41378#ifdef OOMPH_HAS_MPI
41379 // Counter for sub_boundaries
41380 unsigned nsub_boundaries = 0;
41381#endif // #ifdef OOMPH_HAS_MPI
41382
41383 // Continue until all the face elements have been sorted
41384 // This while is to deal with the cases of splitted boundaries
41385 while (nsorted_face_elements < nnon_halo_face_element)
41386 {
41387 // Get and initial face element
41388 FiniteElement* ele_face_pt = 0;
41389#ifdef PARANOID
41390 bool found_initial_face_element = false;
41391#endif
41392
41393 unsigned iface = 0;
41394 for (iface = 0; iface < nnon_halo_face_element; iface++)
41395 {
41396 ele_face_pt = non_halo_face_element_pt[iface];
41397 // If not done then take it as initial face element
41398 if (!face_element_done[ele_face_pt])
41399 {
41400#ifdef PARANOID
41401 found_initial_face_element = true;
41402#endif
41403 nsorted_face_elements++;
41404 iface++;
41405 break;
41406 }
41407 }
41408
41409#ifdef PARANOID
41410 if (!found_initial_face_element)
41411 {
41412 std::ostringstream error_message;
41413 error_message << "Could not find an initial face element for the "
41414 "current segment\n";
41415 // << "----- Possible memory leak -----\n";
41416 throw OomphLibError(
41417 error_message.str(),
41418 "RefineableTriangleMesh::update_polygon_after_restart()",
41419 OOMPH_EXCEPTION_LOCATION);
41420 }
41421#endif
41422
41423 // Local set of coordinates that are on the boundary
41424 // Set entries are ordered on first entry in vector which stores
41425 // the boundary coordinate so the vertices come out in order!
41426 std::set<Vector<double>> local_vertex_nodes;
41427
41428 // Vector to store the vertices, transfer the sorted vertices from the
41429 // set (local) to this vector (local), --- including the z-value ---
41430 Vector<Vector<double>> local_tmp_vector_vertex_node;
41431
41432 // ------------------------------------------------------------------
41433 // ------------------------------------------------------------------
41434 // -----------------------------------------------------------------
41435 // Add the vertices of the initial face element to the set of local
41436 // sorted vertices
41437 // -----------------------------------------------------------------
41438 unsigned nnode = ele_face_pt->nnode();
41439 // Add the left-hand node to the set:
41440 // Boundary coordinate
41441 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
41442 vertex_coord[0] = bound_left[0];
41443
41444 // Actual coordinates
41445 for (unsigned i = 0; i < 2; i++)
41446 {
41447 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
41448 }
41449 local_vertex_nodes.insert(vertex_coord);
41450
41451 // Add the right-hand nodes to the set:
41452 // Boundary coordinate
41453 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
41454 bound, bound_right);
41455 vertex_coord[0] = bound_right[0];
41456
41457 // Actual coordinates
41458 for (unsigned i = 0; i < 2; i++)
41459 {
41460 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
41461 }
41462 local_vertex_nodes.insert(vertex_coord);
41463
41464 // The initial and final node on the set
41465 Node* first_node_pt = ele_face_pt->node_pt(0);
41466 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
41467
41468 // Mark the current face element as done
41469 face_element_done[ele_face_pt] = true;
41470
41471 // ------------------------------------------------------------------
41472 // ------------------------------------------------------------------
41473 // ------------------------------------------------------------------
41474
41475 // Continue iterating if a new face element has been added to the
41476 // list
41477 bool face_element_added = false;
41478
41479 // While a new face element has been added to the set of sorted
41480 // face elements then re-iterate
41481 do
41482 {
41483 // Start from the next face elements since we have already added
41484 // the previous one as the initial face element (any previous face
41485 // element had to be added on previous iterations)
41486 for (unsigned iiface = iface; iiface < nnon_halo_face_element;
41487 iiface++)
41488 {
41489 face_element_added = false;
41490 ele_face_pt = non_halo_face_element_pt[iiface];
41491 if (!face_element_done[ele_face_pt])
41492 {
41493 // Get each individual node to check if they are contiguous
41494 nnode = ele_face_pt->nnode();
41495 Node* left_node_pt = ele_face_pt->node_pt(0);
41496 Node* right_node_pt = ele_face_pt->node_pt(nnode - 1);
41497
41498 if (left_node_pt == first_node_pt)
41499 {
41500 first_node_pt = right_node_pt;
41501 face_element_added = true;
41502 }
41503 else if (left_node_pt == last_node_pt)
41504 {
41505 last_node_pt = right_node_pt;
41506 face_element_added = true;
41507 }
41508 else if (right_node_pt == first_node_pt)
41509 {
41510 first_node_pt = left_node_pt;
41511 face_element_added = true;
41512 }
41513 else if (right_node_pt == last_node_pt)
41514 {
41515 last_node_pt = left_node_pt;
41516 face_element_added = true;
41517 }
41518
41519 if (face_element_added)
41520 {
41521 // Add the left-hand node to the set:
41522 // Boundary coordinate
41523 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
41524 vertex_coord[0] = bound_left[0];
41525
41526 // Actual coordinates
41527 for (unsigned i = 0; i < 2; i++)
41528 {
41529 vertex_coord[i + 1] = left_node_pt->x(i);
41530 }
41531 local_vertex_nodes.insert(vertex_coord);
41532
41533 // Add the right-hand nodes to the set:
41534 // Boundary coordinate
41535 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
41536 vertex_coord[0] = bound_right[0];
41537
41538 // Actual coordinates
41539 for (unsigned i = 0; i < 2; i++)
41540 {
41541 vertex_coord[i + 1] = right_node_pt->x(i);
41542 }
41543 local_vertex_nodes.insert(vertex_coord);
41544
41545 // Mark as done only if one of its nodes has been
41546 // added to the list
41547 face_element_done[ele_face_pt] = true;
41548 nsorted_face_elements++;
41549
41550 break;
41551 }
41552
41553 } // if (!edge_done[edge])
41554 } // for (iiedge < nedges)
41555 } while (face_element_added &&
41556 (nsorted_face_elements < nnon_halo_face_element));
41557
41558 // -----------------------------------------------------------------
41559 // At this point we already have a sorted set of nodes and
41560 // can be used to peform the unrefinement and refinement procedures
41561 // -----------------------------------------------------------------
41562
41563 // Get the number of nodes on the list
41564 const unsigned nlocal_nodes = local_vertex_nodes.size();
41565 // Change representation to vector for easy of handling ...
41566 local_tmp_vector_vertex_node.resize(nlocal_nodes);
41567
41568 // Copy the vertices of the nodes
41569 unsigned counter = 0;
41570 std::set<Vector<double>>::iterator it_vertex;
41571 for (it_vertex = local_vertex_nodes.begin();
41572 it_vertex != local_vertex_nodes.end();
41573 it_vertex++)
41574 {
41575 local_tmp_vector_vertex_node[counter].resize(3);
41576 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
41577 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
41578 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
41579 counter++;
41580 }
41581
41582 // *********************************************************************
41583 // 3) Create the vertices along the boundary using the target area to
41584 // define the distance among them
41585 // *********************************************************************
41586
41587 // Clear the local containter to recover the nodes ordered using the
41588 // zeta value
41589 local_vertex_nodes.clear();
41590
41591 // At the end of each unrefinement/refinement step store the new nodes
41592 // on the set that will give rise to the vertices of the new polyline
41593 // representation
41594 unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
41595 for (unsigned i = 0; i < nnew_nodes; i++)
41596 {
41597 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
41598 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
41599 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
41600 vertex_nodes.insert(vertex_coord); // Global container
41601 local_vertex_nodes.insert(vertex_coord);
41602 }
41603
41604#ifdef OOMPH_HAS_MPI
41605 if (this->is_mesh_distributed())
41606 {
41607 // Add the set of vertices for the boundary, this will help to detect
41608 // if we need to deal with sub_boundaries and sub_polylines represen.
41609 sub_vertex_nodes.push_back(local_vertex_nodes);
41610 // Increase the counter for sub_boundaries
41611 nsub_boundaries++;
41612 }
41613#endif
41614
41615 } // while(nsorted_face_elements < nnon_halo_face_element)
41616
41617 // Now turn into vector for ease of handling...
41618 unsigned npoly_vertex = vertex_nodes.size();
41619 tmp_vector_vertex_node.resize(npoly_vertex);
41620 unsigned count = 0;
41621 std::set<Vector<double>>::iterator it;
41622 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
41623 {
41624 tmp_vector_vertex_node[count].resize(3);
41625 tmp_vector_vertex_node[count][0] = (*it)[0];
41626 tmp_vector_vertex_node[count][1] = (*it)[1];
41627 tmp_vector_vertex_node[count][2] = (*it)[2];
41628 ++count;
41629 }
41630
41631#ifdef OOMPH_HAS_MPI
41632 // --------- Stuff for the sub_boundaries ----- Begin section ---------
41633#ifdef PARANOID
41634 unsigned nsub_boundaries_set = sub_vertex_nodes.size();
41635 if (nsub_boundaries_set != nsub_boundaries)
41636 {
41637 std::ostringstream error_message;
41638 error_message
41639 << "The number of found sub-boundaries and the number of counted\n"
41640 << "sub-boundaries are different:\n"
41641 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
41642 << "Number of counted sub-boundaries: (" << nsub_boundaries << ")\n";
41643 throw OomphLibError(
41644 error_message.str(),
41645 "RefineableTriangleMesh::update_polygon_after_restart()",
41646 OOMPH_EXCEPTION_LOCATION);
41647 }
41648#endif
41649
41650 // Verify if need to deal with sub_boundaries
41651 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41652 {
41653 // Mark the boundary as been splitted in the partition process
41654 this->Boundary_was_splitted[bound] = true;
41655 // Resize the vector to store the info. of sub-boundaries
41656 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
41657 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41658 {
41659 // Turn info. into vector for ease of handling...
41660 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
41661 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
41662 unsigned subcount = 0;
41663 std::set<Vector<double>>::iterator subit;
41664 for (subit = sub_vertex_nodes[isub].begin();
41665 subit != sub_vertex_nodes[isub].end();
41666 ++subit)
41667 {
41668 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
41669 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
41670 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
41671 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
41672 ++subcount;
41673 }
41674 }
41675 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41676 // --------- Stuff for the sub_boundaries ----- End section ------------
41677#endif // OOMPH_HAS_MPI
41678
41679
41680 // For further processing the three-dimensional vector
41681 // has to be reduced to a two-dimensional vector
41682 unsigned n_vertex = tmp_vector_vertex_node.size();
41683
41684 // Resize the vector for vectices
41685 vector_vertex_node.resize(n_vertex);
41686 for (unsigned i = 0; i < n_vertex; i++)
41687 {
41688 vector_vertex_node[i].resize(2);
41689 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
41690 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
41691 }
41692
41693#ifdef OOMPH_HAS_MPI
41694 // --------- Stuff for the sub_boundaries ----- Begin section ----------
41695 // Verify if need to deal with sub_boundaries
41696 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41697 {
41698 // For further processing the three-dimensional vector
41699 // has to be reduced to a two-dimensional vector
41700 // Resize the vector to store the info. of sub-boundaries
41701 sub_vector_vertex_node.resize(nsub_boundaries);
41702 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41703 {
41704 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
41705 // Resize the vector for vectices
41706 sub_vector_vertex_node[isub].resize(subn_vertex);
41707 for (unsigned i = 0; i < subn_vertex; i++)
41708 {
41709 sub_vector_vertex_node[isub][i].resize(2);
41710 sub_vector_vertex_node[isub][i][0] =
41711 sub_tmp_vector_vertex_node[isub][i][1];
41712 sub_vector_vertex_node[isub][i][1] =
41713 sub_tmp_vector_vertex_node[isub][i][2];
41714 }
41715 }
41716 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
41717
41718 // We already have the info. for the sub-boundaries (if necessary)
41719 // and then we can create the sub-boundaries representations to
41720 // ease the generation of the mesh by Triangle
41721
41722 // --------- Stuff for the sub_boundaries ----- End section ------------
41723#endif // OOMPH_HAS_MPI
41724
41725 // *********************************************************************
41726 // 4) Check for contiguousness
41727 // *********************************************************************
41728#ifdef OOMPH_HAS_MPI
41729 // Only perform this checking if the mesh is not distributed
41730 // When the mesh is distributed the polylines continuity is
41731 // addressed with the sort_polylines_helper() method
41732 if (!this->is_mesh_distributed())
41733#endif
41734 {
41735 if (p > 0)
41736 {
41737 // Final end point of previous line
41738 Vector<double> final_vertex_of_previous_segment;
41739 unsigned n_prev_vertex =
41740 polygon_pt->curve_section_pt(p - 1)->nvertex();
41741 final_vertex_of_previous_segment =
41742 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(n_prev_vertex -
41743 1);
41744
41745 unsigned prev_seg_boundary_id =
41746 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41747
41748 // Find the error between the final vertex of the previous
41749 // line and the first vertex of the current line
41750 double error = 0.0;
41751 for (unsigned i = 0; i < 2; i++)
41752 {
41753 const double dist = final_vertex_of_previous_segment[i] -
41754 (*vector_vertex_node.begin())[i];
41755 error += dist * dist;
41756 }
41757 error = sqrt(error);
41758
41759 // If the error is bigger than the tolerance then
41760 // we probably need to reverse, but better check
41762 {
41763 // Find the error between the final vertex of the previous
41764 // line and the last vertex of the current line
41765 double rev_error = 0.0;
41766 for (unsigned i = 0; i < 2; i++)
41767 {
41768 const double dist = final_vertex_of_previous_segment[i] -
41769 (*--vector_vertex_node.end())[i];
41770 rev_error += dist * dist;
41771 }
41772 rev_error = sqrt(rev_error);
41773
41774 if (rev_error >
41776 {
41777 // It could be possible that the first segment be reversed and we
41778 // did not notice it because this check does not apply for the
41779 // first segment. We can verify if the first segment is reversed
41780 // by using the vertex number 1
41781 if (p == 1)
41782 {
41783 // Initial end point of previous line
41784 Vector<double> initial_vertex_of_previous_segment;
41785
41786 initial_vertex_of_previous_segment =
41787 polygon_pt->polyline_pt(p - 1)->vertex_coordinate(0);
41788
41789 unsigned prev_seg_boundary_id =
41790 polygon_pt->curve_section_pt(p - 1)->boundary_id();
41791
41792 // Find the error between the initial vertex of the previous
41793 // line and the first vertex of the current line
41794 double error = 0.0;
41795 for (unsigned i = 0; i < 2; i++)
41796 {
41797 const double dist = initial_vertex_of_previous_segment[i] -
41798 (*vector_vertex_node.begin())[i];
41799 error += dist * dist;
41800 }
41801 error = sqrt(error); // Reversed only the previous one
41802
41803 // If the error is bigger than the tolerance then
41804 // we probably need to reverse, but better check
41805 if (error >
41807 {
41808 // Find the error between the final vertex of the previous
41809 // line and the last vertex of the current line
41810 double rev_error = 0.0;
41811 for (unsigned i = 0; i < 2; i++)
41812 {
41813 const double dist = initial_vertex_of_previous_segment[i] -
41814 (*--vector_vertex_node.end())[i];
41815 rev_error += dist * dist;
41816 }
41817 rev_error =
41818 sqrt(rev_error); // Reversed both the current one and
41819 // the previous one
41820
41821 if (rev_error >
41823 {
41824 std::ostringstream error_stream;
41825 error_stream
41826 << "The distance between the first node of the current\n"
41827 << "line segment (boundary " << bound
41828 << ") and either end of "
41829 << "the previous line segment\n"
41830 << "(boundary " << prev_seg_boundary_id
41831 << ") is bigger than "
41832 << "the desired tolerance "
41834 << ".\n"
41835 << "This suggests that the polylines defining the "
41836 "polygonal\n"
41837 << "representation are not properly ordered.\n"
41838 << "Fail on last vertex of polyline: ("
41839 << prev_seg_boundary_id
41840 << ") and\nfirst vertex of polyline (" << bound
41841 << ").\nThis should have failed when first trying to"
41842 << " construct the\npolygon.\n";
41843 throw OomphLibError(
41844 error_stream.str(),
41845 "RefineableTriangleMesh::update_polygon_after_restart()",
41846 OOMPH_EXCEPTION_LOCATION);
41847 }
41848 else
41849 {
41850 // Reverse both
41851 // Reverse the current vector to line up with the previous
41852 // one
41853 std::reverse(vector_vertex_node.begin(),
41854 vector_vertex_node.end());
41855 polygon_pt->polyline_pt(p - 1)->reverse();
41856 }
41857 }
41858 else
41859 {
41860 // Reverse the previous one
41861 polygon_pt->polyline_pt(p - 1)->reverse();
41862 }
41863
41864 } // if p == 1
41865 else
41866 {
41867 std::ostringstream error_stream;
41868 error_stream
41869 << "The distance between the first node of the current\n"
41870 << "line segment (boundary " << bound
41871 << ") and either end of "
41872 << "the previous line segment\n"
41873 << "(boundary " << prev_seg_boundary_id
41874 << ") is bigger than the "
41875 << "desired tolerance "
41877 << ".\n"
41878 << "This suggests that the polylines defining the polygonal\n"
41879 << "representation are not properly ordered.\n"
41880 << "Fail on last vertex of polyline: ("
41881 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
41882 << bound << ").\n"
41883 << "This should have failed when first trying to construct "
41884 "the\n"
41885 << "polygon.\n";
41886 throw OomphLibError(
41887 error_stream.str(),
41888 "RefineableTriangleMesh::update_polygon_after_restart()",
41889 OOMPH_EXCEPTION_LOCATION);
41890 }
41891 }
41892 else
41893 {
41894 // Reverse the current vector to line up with the previous one
41895 std::reverse(vector_vertex_node.begin(),
41896 vector_vertex_node.end());
41897 }
41898 } // error
41899 } // p > 0
41900 } // is mesh not distributed
41901
41902 // *********************************************************************
41903 // 5) Update the polylines representation
41904 // *********************************************************************
41905 // if (applied_area_length_constraint)
41906 // If only applied when there is a change then it keeps the
41907 // previous polyline representation, it means, it does not delete
41908 // the boundaries that are not part of the domain. We must update
41909 // the boundary representation
41910 {
41911 n_vertex = vector_vertex_node.size();
41912
41913 // Now update the polyline according to the new vertices
41914 // The new one representation
41915 TriangleMeshPolyLine* tmp_polyline_pt =
41916 new TriangleMeshPolyLine(vector_vertex_node, bound);
41917
41918 // for (unsigned h = 0; h < vector_vertex_node.size(); h++)
41919 // {
41920 // DEBP(h);
41921 // DEBP(vector_vertex_node[h][0]);
41922 // DEBP(vector_vertex_node[h][1]);
41923 // }
41924
41925 // Create a temporal "curve section" version of the recently created
41926 // polyline
41927 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
41928
41929 // Tolerance below which the middle point can be deleted
41930 // (ratio of deflection to element length)
41931 double unrefinement_tolerance =
41932 polygon_pt->polyline_pt(p)->unrefinement_tolerance();
41933
41934 // Tolerance to add points
41935 double refinement_tolerance =
41936 polygon_pt->polyline_pt(p)->refinement_tolerance();
41937
41938 // Establish refinement and unrefinement tolerance
41939 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
41940 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
41941
41942 // Establish the maximum length constraint
41943 double maximum_length = polygon_pt->polyline_pt(p)->maximum_length();
41944 tmp_polyline_pt->set_maximum_length(maximum_length);
41945
41946 if (n_vertex >= 2)
41947 {
41948 // Pass the connection information from the old polyline to the
41949 // new one
41950 this->copy_connection_information(polygon_pt->polyline_pt(p),
41951 tmp_curve_section_pt);
41952 }
41953
41954 // Now update the polyline according to the new vertices but
41955 // first check if the object is allowed to delete the representation
41956 // or if it should be done by other object
41957 bool delete_it_on_destructor = false;
41958
41959 std::set<TriangleMeshCurveSection*>::iterator it =
41960 this->Free_curve_section_pt.find(polygon_pt->curve_section_pt(p));
41961
41962 if (it != this->Free_curve_section_pt.end())
41963 {
41964 this->Free_curve_section_pt.erase(it);
41965 delete polygon_pt->curve_section_pt(p);
41966 delete_it_on_destructor = true;
41967 }
41968
41969 // *****************************************************************
41970 // Copying the new representation
41971 polygon_pt->curve_section_pt(p) = tmp_polyline_pt;
41972
41973 // Update the Boundary - Polyline map
41974 this->Boundary_curve_section_pt[bound] =
41975 polygon_pt->curve_section_pt(p);
41976
41977 if (delete_it_on_destructor)
41978 {
41979 this->Free_curve_section_pt.insert(polygon_pt->curve_section_pt(p));
41980 }
41981
41982#ifdef OOMPH_HAS_MPI
41983 // --------- Stuff for the sub_boundaries ----- Begin section --------
41984 // Verify if need to deal with sub_boundaries
41985 if (this->is_mesh_distributed() && nsub_boundaries > 1)
41986 {
41987 // Create temporary representations for the boundaries, only to
41988 // create the mesh when calling Triangle
41989 // Clear all previous stored data
41990 this->Boundary_subpolylines[bound].clear();
41991 // Now create storage for the sub-boundaries
41992 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
41993 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
41994 {
41995 // Now update the polyline according to the sub set of
41996 // vertices, set the chunk number of the polyline
41997 TriangleMeshPolyLine* sub_tmp_polyline_pt =
41999 sub_vector_vertex_node[isub], bound, isub);
42000
42001 // Add the sub-polyline to the container to represent the boundary
42002 // in parts
42003 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
42004
42005 // No need to send the unrefinement/refinement and maximum
42006 // length constraints since these are only temporary
42007 // representations. These polylines can be deleted once the
42008 // new polygons that represent the distributed domain have
42009 // been created
42010
42011 } // for (isub < nsub_boundaries)
42012
42013 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42014 // --------- Stuff for the sub_boundaries ----- End section ---------
42015#endif // OOMPH_HAS_MPI
42016
42017 } // update polyline representation
42018
42019 // Delete the allocated memory for the geometric object that
42020 // represents the curvilinear boundary
42021 delete mesh_geom_obj_pt;
42022
42023 } // npolyline
42024
42025 // Cleanup the face mesh
42026 for (unsigned p = 0; p < n_polyline; p++)
42027 {
42028 face_mesh_pt[p]->flush_node_storage();
42029 delete face_mesh_pt[p];
42030 }
42031 }
42032
42033
42034 //======================================================================
42035 /// Updates the open curve representation after restart
42036 //======================================================================
42037 template<class ELEMENT>
42039 TriangleMeshOpenCurve*& open_curve_pt)
42040 {
42041 // **********************************************************************
42042 // 1) Get the vertices along the boundaries ids of the polylines and
42043 // update them
42044 // **********************************************************************
42045
42046 // (1.1) Get the face mesh representation
42047 Vector<Mesh*> face_mesh_pt;
42048 get_face_mesh_representation(open_curve_pt, face_mesh_pt);
42049
42050 // (1.2) Create vertices of the polylines by using the vertices of the
42051 // FaceElements
42052 Vector<double> vertex_coord(3); // zeta,x,y
42053 Vector<double> bound_left(1);
42054 Vector<double> bound_right(1);
42055
42056 const unsigned ncurve_section = open_curve_pt->ncurve_section();
42057 // Go for each curve section
42058 for (unsigned cs = 0; cs < ncurve_section; cs++)
42059 {
42060 // Get the MeshAsGeomObject representation just once per polyline,
42061 // this object is only used by the
42062 // refine_boundary_constrained_by_target_area() method. We get it here
42063 // to ensure that all processors (in a distributed context) get this
42064 // representation just once, and because an AllToAll MPI communication
42065 // is used in this calling
42066 MeshAsGeomObject* mesh_geom_obj_pt =
42067 new MeshAsGeomObject(face_mesh_pt[cs]);
42068
42069 // Get the boundary id
42070 const unsigned bound = open_curve_pt->curve_section_pt(cs)->boundary_id();
42071
42072 /// Use a vector of vector for vertices and target areas to deal
42073 /// with the cases when the boundaries are split bn the
42074 /// distribution process. Internal boundaries may be completely or
42075 /// partially overlapped by shared boundaries
42076
42077 // Loop over the face elements and add their vertices (they are
42078 // automatically sorted because of the set)
42079 const unsigned nface_element = face_mesh_pt[cs]->nelement();
42080 // Store the non halo elements and the element at the other side of
42081 // the boundary (whatever it be halo or not), the first will be the
42082 // ones from which we will get the vertices (in even position)
42083 Vector<FiniteElement*> non_halo_doubled_face_element_pt;
42084
42085 // Map to store the index of the face element on a boundary
42086 std::map<FiniteElement*, unsigned> face_element_index_on_boundary;
42087
42088 // Map to know the already sorted face elements
42089 std::map<FiniteElement*, bool> face_element_done;
42090
42091 for (unsigned ef = 0; ef < nface_element; ++ef)
42092 {
42093 FiniteElement* ele_face_pt = face_mesh_pt[cs]->finite_element_pt(ef);
42094
42095 // Skip the halo elements (not used as base elements, only
42096 // include those elements which one of its counterparts -- at the
42097 // other side of the boundary -- is non halo)
42098#ifdef OOMPH_HAS_MPI
42099 if (this->is_mesh_distributed())
42100 {
42101 // Only work with non-halo elements
42102 if (ele_face_pt->is_halo())
42103 {
42104 continue;
42105 }
42106 }
42107#endif
42108
42109 // Check if not already done
42110 if (!face_element_done[ele_face_pt])
42111 {
42112 // Add the element and look for the element at the other side
42113 // of the boundary to add it immediately after the new added
42114 // element
42115 non_halo_doubled_face_element_pt.push_back(ele_face_pt);
42116 // Create the map of the face element with the index
42117 face_element_index_on_boundary[ele_face_pt] = ef;
42118 // Mark the current element as done
42119 face_element_done[ele_face_pt] = true;
42120 // Get the number of nodes
42121 const unsigned nnodes = ele_face_pt->nnode();
42122 // Get the left and right node to look for the elements at the
42123 // other side of the boundary
42124 Node* left_node_pt = ele_face_pt->node_pt(0);
42125 Node* right_node_pt = ele_face_pt->node_pt(nnodes - 1);
42126
42127#ifdef PARANOID
42128 // Flag to know if the element at the other side of the
42129 // boundary was found
42130 bool found_other_side_face_ele = false;
42131#endif
42132 for (unsigned iface = 0; iface < nface_element; iface++)
42133 {
42134 // Get the candidate face element
42135 FiniteElement* cele_face_pt =
42136 face_mesh_pt[cs]->finite_element_pt(iface);
42137 // Check if not already done
42138 if (!face_element_done[cele_face_pt])
42139 {
42140 Node* cleft_node_pt = cele_face_pt->node_pt(0);
42141 Node* cright_node_pt = cele_face_pt->node_pt(nnodes - 1);
42142
42143 // Check if the nodes are the same
42144 if ((left_node_pt == cleft_node_pt &&
42145 right_node_pt == cright_node_pt) ||
42146 (left_node_pt == cright_node_pt &&
42147 right_node_pt == cleft_node_pt))
42148 {
42149 // Add the element to the storage
42150 non_halo_doubled_face_element_pt.push_back(cele_face_pt);
42151 // ... and mark the element as done
42152 face_element_done[cele_face_pt] = true;
42153 // Create the map of the face element with the index
42154 face_element_index_on_boundary[cele_face_pt] = iface;
42155#ifdef PARANOID
42156 // Set the flag of found other side face element
42157 found_other_side_face_ele = true;
42158#endif
42159 break;
42160 }
42161 }
42162 } // (iface < nface_element)
42163
42164#ifdef PARANOID
42165 if (!found_other_side_face_ele)
42166 {
42167 std::ostringstream error_message;
42168 error_message
42169 << "The face element at the other side of the boundary (" << bound
42170 << ") was not found!!\n"
42171 << "These are the nodes of the face element:\n"
42172 << "(" << left_node_pt->x(0) << ", " << left_node_pt->x(1) << ") "
42173 << "and (" << right_node_pt->x(0) << "," << right_node_pt->x(1)
42174 << ")\n\n";
42175 throw OomphLibError(
42176 error_message.str(),
42177 "RefineableTriangleMesh::update_open_curve_after_restart()",
42178 OOMPH_EXCEPTION_LOCATION);
42179 }
42180#endif
42181 } // if (!face_ele_done[ele_face_pt])
42182
42183 } // (ef < nface_element)
42184
42185 // Clear the map of the already done face elements
42186 // This will now be used to sort the face elements
42187 face_element_done.clear();
42188
42189 // Set of coordinates that are on the boundary
42190 // The entries are sorted on first entry in vector which stores
42191 // the boundary coordinate so the vertices come out in order!
42192 std::set<Vector<double>> vertex_nodes;
42193
42194 // Vector to store the vertices, transfer the sorted vertices from the
42195 // set to this vector, --- including the z-value ---
42196 Vector<Vector<double>> tmp_vector_vertex_node;
42197
42198 // Vector to store the coordinates of the polylines, same as the
42199 // tmp_vector_vertex_node vector (after adding more nodes) but
42200 // --- without the z-value ---, used to re-generate the polylines
42201 Vector<Vector<double>> vector_vertex_node;
42202
42203#ifdef OOMPH_HAS_MPI
42204 // Indicates if the set of vertices give rise to a internal
42205 // boundary that will be used as shared boundary or as normal
42206 // internal boundary -- Only used to deal with internal boundaries
42207 // in a distributed scheme
42208 std::vector<bool> internal_to_shared_boundary;
42209
42210 // --------- Stuff to deal with splitted boundaries ---------- Begin -----
42211 // Set of coordinates that are on the boundary (splitted boundary version)
42212 // The first vector is used to allocate the points for each sub-boundary
42213 // Set entries are ordered on first entry in vector which stores
42214 // the boundary coordinate so the vertices come out in order!
42215 Vector<std::set<Vector<double>>> sub_vertex_nodes;
42216
42217 // Vector to store the vertices, transfer the sorted vertices from the
42218 // set (sub_vertex_nodes) to this vector, --- including the z-value ---
42219 Vector<Vector<Vector<double>>> sub_tmp_vector_vertex_node;
42220
42221 // Vector to store the coordinates of the polylines that will represent
42222 // the splitted boundary. Used to pass the info. from sub_vertex_nodes
42223 // but --- without the z-value ---, used to generate the sub-polylines
42224 Vector<Vector<Vector<double>>> sub_vector_vertex_node;
42225
42226 // --------- Stuff to deal with splitted boundaries ----------- End ------
42227#endif
42228
42229 // Sort face elements, separate those with both nonhalo face
42230 // elements from those with one halo and one nonhalo face element
42231
42232 // Number of done face elements
42233 unsigned nsorted_face_elements = 0;
42234
42235#ifdef OOMPH_HAS_MPI
42236 // Counter for sub_boundaries
42237 unsigned nsub_boundaries = 0;
42238#endif // #ifdef OOMPH_HAS_MPI
42239
42240 // Total number of non halo double face element
42241 const unsigned nnon_halo_doubled_face_ele =
42242 non_halo_doubled_face_element_pt.size();
42243
42244 // Continue until all the face elements have been sorted
42245 // This while is to deal with the cases of splitted boundaries
42246 while (nsorted_face_elements < nnon_halo_doubled_face_ele)
42247 {
42248 // Get and initial face element
42249 FiniteElement* ele_face_pt = 0;
42250 FiniteElement* repeated_ele_face_pt = 0;
42251#ifdef PARANOID
42252 bool found_initial_face_element = false;
42253#endif
42254
42255 // Flag to know if we are working with a face element which the
42256 // face element at the other side of the boundary is also non
42257 // halo
42258 bool both_root_face_elements_are_nonhalo = false;
42259
42260 unsigned iface = 0;
42261 for (iface = 0; iface < nnon_halo_doubled_face_ele; iface += 2)
42262 {
42263 ele_face_pt = non_halo_doubled_face_element_pt[iface];
42264 // If not done then take it as initial face element
42265 if (!face_element_done[ele_face_pt])
42266 {
42267 // Mark it as done
42268 face_element_done[ele_face_pt] = true;
42269 // Get the other side boundary face element
42270 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iface + 1];
42271 // ... also mark as done the repeated face element
42272 face_element_done[repeated_ele_face_pt] = true;
42273
42274#ifdef OOMPH_HAS_MPI
42275 if (!repeated_ele_face_pt->is_halo())
42276 {
42277 both_root_face_elements_are_nonhalo = true;
42278 }
42279#endif // #ifdef OOMPH_HAS_MPI
42280
42281 // Plus two because internal boundaries have
42282 // two face elements per each edge
42283 nsorted_face_elements += 2;
42284 iface += 2;
42285#ifdef PARANOID
42286 // And set the flag to true
42287 found_initial_face_element = true;
42288#endif
42289 break;
42290 }
42291 }
42292
42293#ifdef PARANOID
42294 if (!found_initial_face_element)
42295 {
42296 std::ostringstream error_message;
42297 error_message << "Could not find an initial face element for the "
42298 "current segment\n";
42299 // << "----- Possible memory leak -----\n";
42300 throw OomphLibError(error_message.str(),
42301 OOMPH_CURRENT_FUNCTION,
42302 OOMPH_EXCEPTION_LOCATION);
42303 }
42304#endif
42305
42306 // Local set of coordinates that are on the boundary Set entries
42307 // are ordered on first entry in vector which stores the boundary
42308 // coordinate so the vertices come out in order
42309 std::set<Vector<double>> local_vertex_nodes;
42310
42311 // Vector to store the vertices, transfer the sorted vertices from the
42312 // set (local) to this vector (local), --- including the z-value ---
42313 Vector<Vector<double>> local_tmp_vector_vertex_node;
42314
42315 // ------------------------------------------------------------------
42316 // ------------------------------------------------------------------
42317 // Add the vertices of the initial face element to the set of local
42318 // sorted vertices
42319 // ------------------------------------------------------------------
42320 // ------------------------------------------------------------------
42321 const unsigned nnode = ele_face_pt->nnode();
42322 // Add the left-hand node to the set:
42323 // Boundary coordinate
42324 ele_face_pt->node_pt(0)->get_coordinates_on_boundary(bound, bound_left);
42325 vertex_coord[0] = bound_left[0];
42326
42327 // Actual coordinates
42328 for (unsigned i = 0; i < 2; i++)
42329 {
42330 vertex_coord[i + 1] = ele_face_pt->node_pt(0)->x(i);
42331 }
42332 local_vertex_nodes.insert(vertex_coord);
42333
42334 // Add the right-hand node to the set:
42335 // Boundary coordinate
42336 ele_face_pt->node_pt(nnode - 1)->get_coordinates_on_boundary(
42337 bound, bound_right);
42338 vertex_coord[0] = bound_right[0];
42339
42340 // Actual coordinates
42341 for (unsigned i = 0; i < 2; i++)
42342 {
42343 vertex_coord[i + 1] = ele_face_pt->node_pt(nnode - 1)->x(i);
42344 }
42345 local_vertex_nodes.insert(vertex_coord);
42346
42347 // The initial and final node on the set
42348 Node* first_node_pt = ele_face_pt->node_pt(0);
42349 Node* last_node_pt = ele_face_pt->node_pt(nnode - 1);
42350
42351 // Continue iterating if a new face element has been added to the
42352 // list
42353 bool face_element_added = false;
42354
42355 // While a new face element has been added to the set of sorted
42356 // face elements then re-iterate
42357 do
42358 {
42359 // Start from the next face elements since we have already
42360 // added the previous one as the initial face element (any
42361 // previous face element had to be added on previous
42362 // iterations)
42363 for (unsigned iiface = iface; iiface < nnon_halo_doubled_face_ele;
42364 iiface += 2)
42365 {
42366 face_element_added = false;
42367 ele_face_pt = non_halo_doubled_face_element_pt[iiface];
42368
42369 // Check that the face element with which we are working has
42370 // the same conditions as the root face element (both faces
42371 // are nonhalo or one face is halo and the other nonhalo)
42372
42373 // Get the face element at the other side of the boundary
42374 repeated_ele_face_pt = non_halo_doubled_face_element_pt[iiface + 1];
42375 bool both_face_elements_are_nonhalo = false;
42376
42377#ifdef OOMPH_HAS_MPI
42378 if (!repeated_ele_face_pt->is_halo())
42379 {
42380 both_face_elements_are_nonhalo = true;
42381 }
42382#endif // #ifdef OOMPH_HAS_MPI
42383
42384 if (!face_element_done[ele_face_pt] &&
42385 (both_face_elements_are_nonhalo ==
42386 both_root_face_elements_are_nonhalo))
42387 {
42388 // Get each individual node to check if they are contiguous
42389 const unsigned nlnode = ele_face_pt->nnode();
42390 Node* left_node_pt = ele_face_pt->node_pt(0);
42391 Node* right_node_pt = ele_face_pt->node_pt(nlnode - 1);
42392
42393 if (left_node_pt == first_node_pt)
42394 {
42395 first_node_pt = right_node_pt;
42396 face_element_added = true;
42397 }
42398 else if (left_node_pt == last_node_pt)
42399 {
42400 last_node_pt = right_node_pt;
42401 face_element_added = true;
42402 }
42403 else if (right_node_pt == first_node_pt)
42404 {
42405 first_node_pt = left_node_pt;
42406 face_element_added = true;
42407 }
42408 else if (right_node_pt == last_node_pt)
42409 {
42410 last_node_pt = left_node_pt;
42411 face_element_added = true;
42412 }
42413
42414 if (face_element_added)
42415 {
42416 // Add the left-hand node to the set:
42417 // Boundary coordinate
42418 left_node_pt->get_coordinates_on_boundary(bound, bound_left);
42419 vertex_coord[0] = bound_left[0];
42420
42421 // Actual coordinates
42422 for (unsigned i = 0; i < 2; i++)
42423 {
42424 vertex_coord[i + 1] = left_node_pt->x(i);
42425 }
42426 local_vertex_nodes.insert(vertex_coord);
42427
42428 // Add the right-hand nodes to the set:
42429 // Boundary coordinate
42430 right_node_pt->get_coordinates_on_boundary(bound, bound_right);
42431 vertex_coord[0] = bound_right[0];
42432
42433 // Actual coordinates
42434 for (unsigned i = 0; i < 2; i++)
42435 {
42436 vertex_coord[i + 1] = right_node_pt->x(i);
42437 }
42438 local_vertex_nodes.insert(vertex_coord);
42439
42440 // Mark as done only if one of its nodes has been
42441 // added to the list
42442 face_element_done[ele_face_pt] = true;
42443 // .. also mark as done the face element at the othe side of
42444 // the boundary
42445 repeated_ele_face_pt =
42446 non_halo_doubled_face_element_pt[iiface + 1];
42447 face_element_done[repeated_ele_face_pt] = true;
42448 // ... and increase the number of sorted face elements
42449 nsorted_face_elements += 2;
42450
42451 break;
42452 }
42453
42454 } // if (!face_element_done[[ele_face_pt])
42455 } // for (iiface<nnon_halo_doubled_face_ele)
42456 } while (face_element_added &&
42457 (nsorted_face_elements < nnon_halo_doubled_face_ele));
42458
42459 // -------------------------------------------------------------
42460 // At this point we already have a sorted set of nodes and can
42461 // be used to peform the unrefinement and refinement procedures
42462 // -------------------------------------------------------------
42463
42464 // Get the number of nodes on the list
42465 const unsigned nlocal_nodes = local_vertex_nodes.size();
42466 // Change representation to vector for easy of handling ...
42467 local_tmp_vector_vertex_node.resize(nlocal_nodes);
42468
42469 // Copy the vertices of the nodes
42470 unsigned counter = 0;
42471 std::set<Vector<double>>::iterator it_vertex;
42472 for (it_vertex = local_vertex_nodes.begin();
42473 it_vertex != local_vertex_nodes.end();
42474 it_vertex++)
42475 {
42476 local_tmp_vector_vertex_node[counter].resize(3);
42477 local_tmp_vector_vertex_node[counter][0] = (*it_vertex)[0];
42478 local_tmp_vector_vertex_node[counter][1] = (*it_vertex)[1];
42479 local_tmp_vector_vertex_node[counter][2] = (*it_vertex)[2];
42480 counter++;
42481 }
42482
42483 // The unrefinement and refinement process needs to be applied
42484 // from the bottom-left node since the internal open curve could
42485 // lie on the shared boundaries
42486 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] <
42487 local_tmp_vector_vertex_node[0][2])
42488 {
42489 std::reverse(local_tmp_vector_vertex_node.begin(),
42490 local_tmp_vector_vertex_node.end());
42491 }
42492 else if (local_tmp_vector_vertex_node[nlocal_nodes - 1][2] ==
42493 local_tmp_vector_vertex_node[0][2])
42494 {
42495 if (local_tmp_vector_vertex_node[nlocal_nodes - 1][1] <
42496 local_tmp_vector_vertex_node[0][1])
42497 {
42498 std::reverse(local_tmp_vector_vertex_node.begin(),
42499 local_tmp_vector_vertex_node.end());
42500 }
42501 }
42502
42503 // ****************************************************************
42504 // 3) Create the vertices along the boundary using the target
42505 // area to define the distance among them
42506 // ****************************************************************
42507
42508 // Clear the local containter to recover the nodes ordered using
42509 // the zeta value
42510 local_vertex_nodes.clear();
42511
42512 // At the end of each unrefinement/refinement step store the new
42513 // nodes on the set that will give rise to the vertices of the
42514 // new polyline representation
42515 const unsigned nnew_nodes = local_tmp_vector_vertex_node.size();
42516 for (unsigned i = 0; i < nnew_nodes; i++)
42517 {
42518 vertex_coord[0] = local_tmp_vector_vertex_node[i][0];
42519 vertex_coord[1] = local_tmp_vector_vertex_node[i][1];
42520 vertex_coord[2] = local_tmp_vector_vertex_node[i][2];
42521 vertex_nodes.insert(vertex_coord); // Global container
42522 local_vertex_nodes.insert(vertex_coord);
42523 }
42524
42525#ifdef OOMPH_HAS_MPI
42526 if (this->is_mesh_distributed())
42527 {
42528 // Add the set of vertices for the boundary, this will help to
42529 // detect if we need to deal with sub_boundaries and
42530 // sub_polylines representations
42531 sub_vertex_nodes.push_back(local_vertex_nodes);
42532 // Increase the counter for sub_boundaries
42533 nsub_boundaries++;
42534
42535 // Mark if the polyline created by these vertices will be used
42536 // as a shared boundary or as an internal boundary
42537 if (both_root_face_elements_are_nonhalo)
42538 {
42539 internal_to_shared_boundary.push_back(false);
42540 }
42541 else
42542 {
42543 internal_to_shared_boundary.push_back(true);
42544 }
42545 }
42546#endif
42547
42548 } // while(nsorted_face_elements < nnon_halo_doubled_face_ele)
42549 // This while is in charge of sorting all the face elements to
42550 // create the new representation of the polyline (also deals
42551 // with the sub-boundary cases)
42552
42553 // Now turn into vector for ease of handling...
42554 const unsigned npoly_vertex = vertex_nodes.size();
42555 tmp_vector_vertex_node.resize(npoly_vertex);
42556 unsigned count = 0;
42557 std::set<Vector<double>>::iterator it;
42558 for (it = vertex_nodes.begin(); it != vertex_nodes.end(); ++it)
42559 {
42560 tmp_vector_vertex_node[count].resize(3);
42561 tmp_vector_vertex_node[count][0] = (*it)[0];
42562 tmp_vector_vertex_node[count][1] = (*it)[1];
42563 tmp_vector_vertex_node[count][2] = (*it)[2];
42564 ++count;
42565 }
42566
42567#ifdef OOMPH_HAS_MPI
42568 // Check that the number of set of vertices marked to be shared or
42569 // internal boundaries be the same as the total number of
42570 // sub-boundaries
42571#ifdef PARANOID
42572 const unsigned nsub_boundaries_set = sub_vertex_nodes.size();
42573 const unsigned ninternal_to_shared_boundaries =
42574 internal_to_shared_boundary.size();
42575 if (nsub_boundaries_set != ninternal_to_shared_boundaries)
42576 {
42577 std::ostringstream error_message;
42578 error_message
42579 << "The number of found sub-boundaries and the number of marked "
42580 << "internal\nboundaries are different\n"
42581 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42582 << "Number of marked internal boundaries: ("
42583 << ninternal_to_shared_boundaries << ")\n\n";
42584 throw OomphLibError(
42585 error_message.str(),
42586 "RefineableTriangleMesh::update_open_curve_after_restart()",
42587 OOMPH_EXCEPTION_LOCATION);
42588 }
42589#endif
42590
42591 // --------- Stuff for the sub_boundaries ----- Begin section -------
42592#ifdef PARANOID
42593 if (nsub_boundaries_set != nsub_boundaries)
42594 {
42595 std::ostringstream error_message;
42596 error_message
42597 << "The number of found sub-boundaries and the number of counted\n"
42598 << "sub-boundaries are different:\n"
42599 << "Number of found sub-boundaries: (" << nsub_boundaries_set << ")\n"
42600 << "Number of counted sub-boundaries: (" << nsub_boundaries
42601 << ")\n\n";
42602 throw OomphLibError(
42603 error_message.str(),
42604 "RefineableTriangleMesh::update_open_curve_after_restart()",
42605 OOMPH_EXCEPTION_LOCATION);
42606 }
42607#endif
42608
42609 // Verify if need to deal with sub_boundaries
42610 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42611 {
42612 // Mark the boundary as been splitted in the partition process
42613 this->Boundary_was_splitted[bound] = true;
42614 // Resize the vector to store the info. of sub-boundaries
42615 sub_tmp_vector_vertex_node.resize(nsub_boundaries);
42616 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42617 {
42618 // Turn info. into vector for ease of handling...
42619 const unsigned nsubpoly_vertex = sub_vertex_nodes[isub].size();
42620 sub_tmp_vector_vertex_node[isub].resize(nsubpoly_vertex);
42621 unsigned subcount = 0;
42622 std::set<Vector<double>>::iterator subit;
42623 for (subit = sub_vertex_nodes[isub].begin();
42624 subit != sub_vertex_nodes[isub].end();
42625 ++subit)
42626 {
42627 sub_tmp_vector_vertex_node[isub][subcount].resize(3);
42628 sub_tmp_vector_vertex_node[isub][subcount][0] = (*subit)[0];
42629 sub_tmp_vector_vertex_node[isub][subcount][1] = (*subit)[1];
42630 sub_tmp_vector_vertex_node[isub][subcount][2] = (*subit)[2];
42631 ++subcount;
42632 }
42633 }
42634 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42635 // --------- Stuff for the sub_boundaries ----- End section ----------
42636#endif // OOMPH_HAS_MPI
42637
42638 // For further processing the three-dimensional vector has to be
42639 // reduced to a two-dimensional vector
42640 unsigned n_vertex = tmp_vector_vertex_node.size();
42641
42642 // Resize the vector for vectices
42643 vector_vertex_node.resize(n_vertex);
42644 for (unsigned i = 0; i < n_vertex; i++)
42645 {
42646 vector_vertex_node[i].resize(2);
42647 vector_vertex_node[i][0] = tmp_vector_vertex_node[i][1];
42648 vector_vertex_node[i][1] = tmp_vector_vertex_node[i][2];
42649 }
42650
42651#ifdef OOMPH_HAS_MPI
42652 // --------- Stuff for the sub_boundaries ----- Begin section ----------
42653 // Verify if need to deal with sub_boundaries
42654 if (this->is_mesh_distributed() && nsub_boundaries > 1)
42655 {
42656 // For further processing the three-dimensional vector
42657 // has to be reduced to a two-dimensional vector
42658 // Resize the vector to store the info. of sub-boundaries
42659 sub_vector_vertex_node.resize(nsub_boundaries);
42660 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42661 {
42662 const unsigned subn_vertex = sub_tmp_vector_vertex_node[isub].size();
42663 // Resize the vector for vectices
42664 sub_vector_vertex_node[isub].resize(subn_vertex);
42665 for (unsigned i = 0; i < subn_vertex; i++)
42666 {
42667 sub_vector_vertex_node[isub][i].resize(2);
42668 sub_vector_vertex_node[isub][i][0] =
42669 sub_tmp_vector_vertex_node[isub][i][1];
42670 sub_vector_vertex_node[isub][i][1] =
42671 sub_tmp_vector_vertex_node[isub][i][2];
42672 }
42673 }
42674 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42675
42676 // We already have the info. for the sub-boundaries (if necessary) and
42677 // then we can create the sub-boundaries representations to ease the
42678 // generation of the mesh by Triangle
42679
42680 // --------- Stuff for the sub_boundaries ----- End section ------------
42681#endif // OOMPH_HAS_MPI
42682
42683 // *********************************************************************
42684 // 4) Check for contiguousness
42685 // *********************************************************************
42686#ifdef OOMPH_HAS_MPI
42687 // Only perform this checking if the mesh is not distributed
42688 // When the mesh is distributed the polylines continuity is
42689 // addressed with the sort_polylines_helper() method
42690 if (!this->is_mesh_distributed())
42691#endif
42692 {
42693 if (cs > 0)
42694 {
42695 // Final end point of previous line
42696 Vector<double> final_vertex_of_previous_segment;
42697 unsigned n_prev_vertex =
42698 open_curve_pt->curve_section_pt(cs - 1)->nvertex();
42699 final_vertex_of_previous_segment =
42700 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(
42701 n_prev_vertex - 1);
42702
42703 unsigned prev_seg_boundary_id =
42704 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42705
42706 // Find the error between the final vertex of the previous
42707 // line and the first vertex of the current line
42708 double error = 0.0;
42709 for (unsigned i = 0; i < 2; i++)
42710 {
42711 const double dist = final_vertex_of_previous_segment[i] -
42712 (*vector_vertex_node.begin())[i];
42713 error += dist * dist;
42714 }
42715 error = sqrt(error);
42716
42717 // If the error is bigger than the tolerance then
42718 // we probably need to reverse, but better check
42720 {
42721 // Find the error between the final vertex of the previous
42722 // line and the last vertex of the current line
42723 double rev_error = 0.0;
42724 for (unsigned i = 0; i < 2; i++)
42725 {
42726 const double dist = final_vertex_of_previous_segment[i] -
42727 (*--vector_vertex_node.end())[i];
42728 rev_error += dist * dist;
42729 }
42730 rev_error = sqrt(rev_error);
42731
42732 if (rev_error >
42734 {
42735 // It could be possible that the first segment be reversed and we
42736 // did not notice it because this check does not apply for the
42737 // first segment. We can verify if the first segment is reversed
42738 // by using the vertex number 1
42739 if (cs == 1)
42740 {
42741 // Initial end point of previous line
42742 Vector<double> initial_vertex_of_previous_segment;
42743
42744 initial_vertex_of_previous_segment =
42745 open_curve_pt->polyline_pt(cs - 1)->vertex_coordinate(0);
42746
42747 unsigned prev_seg_boundary_id =
42748 open_curve_pt->curve_section_pt(cs - 1)->boundary_id();
42749
42750 // Find the error between the initial vertex of the previous
42751 // line and the first vertex of the current line
42752 double error = 0.0;
42753 for (unsigned i = 0; i < 2; i++)
42754 {
42755 const double dist = initial_vertex_of_previous_segment[i] -
42756 (*vector_vertex_node.begin())[i];
42757 error += dist * dist;
42758 }
42759 error = sqrt(error); // Reversed only the previous one
42760
42761 // If the error is bigger than the tolerance then
42762 // we probably need to reverse, but better check
42763 if (error >
42765 {
42766 // Find the error between the final vertex of the previous
42767 // line and the last vertex of the current line
42768 double rev_error = 0.0;
42769 for (unsigned i = 0; i < 2; i++)
42770 {
42771 const double dist = initial_vertex_of_previous_segment[i] -
42772 (*--vector_vertex_node.end())[i];
42773 rev_error += dist * dist;
42774 }
42775 rev_error = sqrt(rev_error); // Reversed both the current
42776 // one and the previous one
42777
42778 if (rev_error >
42780 {
42781 std::ostringstream error_stream;
42782 error_stream
42783 << "The distance between the first node of the current\n"
42784 << "line segment (boundary " << bound
42785 << ") and either end of "
42786 << "the previous line segment\n"
42787 << "(boundary " << prev_seg_boundary_id
42788 << ") is bigger than"
42789 << " the desired tolerance "
42791 << ".\n"
42792 << "This suggests that the polylines defining the "
42793 "polygonal\n"
42794 << "representation are not properly ordered.\n"
42795 << "Fail on last vertex of polyline: ("
42796 << prev_seg_boundary_id
42797 << ") and\nfirst vertex of polyline (" << bound
42798 << ").\nThis should have failed when first trying to "
42799 << "construct the\npolygon.\n";
42800 throw OomphLibError(error_stream.str(),
42801 "RefineableTriangleMesh::update_open_"
42802 "curve_after_restart()",
42803 OOMPH_EXCEPTION_LOCATION);
42804 }
42805 else
42806 {
42807 // Reverse both
42808 // Reverse the current vector to line up with the previous
42809 // one
42810 std::reverse(vector_vertex_node.begin(),
42811 vector_vertex_node.end());
42812 open_curve_pt->polyline_pt(cs - 1)->reverse();
42813 }
42814 }
42815 else
42816 {
42817 // Reverse the previous one
42818 open_curve_pt->polyline_pt(cs - 1)->reverse();
42819 }
42820
42821 } // if (cs == 1)
42822 else
42823 {
42824 std::ostringstream error_stream;
42825 error_stream
42826 << "The distance between the first node of the current\n"
42827 << "line segment (boundary " << bound
42828 << ") and either end of "
42829 << "the previous line segment\n"
42830 << "(boundary " << prev_seg_boundary_id
42831 << ") is bigger than the "
42832 << "desired tolerance "
42834 << ".\n"
42835 << "This suggests that the polylines defining the polygonal\n"
42836 << "representation are not properly ordered.\n"
42837 << "Fail on last vertex of polyline: ("
42838 << prev_seg_boundary_id << ") and\nfirst vertex of polyline ("
42839 << bound << ").\n"
42840 << "This should have failed when first trying to construct "
42841 "the\n"
42842 << "polygon.\n";
42843 throw OomphLibError(
42844 error_stream.str(),
42845 "RefineableTriangleMesh::update_open_curve_after_restart()",
42846 OOMPH_EXCEPTION_LOCATION);
42847 }
42848 }
42849 else
42850 {
42851 // Reverse the current vector to line up with the previous one
42852 std::reverse(vector_vertex_node.begin(),
42853 vector_vertex_node.end());
42854 }
42855 } // error
42856 } // (cs > 0)
42857 } // is mesh not distributed
42858
42859 // DEBP(applied_area_length_constraint);
42860 // DEBP(p);
42861 // getchar();
42862 // *********************************************************************
42863 // 5) Update the polylines representation
42864 // *********************************************************************
42865 // if (applied_area_length_constraint)
42866 // If only applied when there is a change then it keeps the
42867 // previous polyline representation, it means, it does not delete
42868 // the boundaries that are not part of the domain. We must update
42869 // the boundary representation
42870 {
42871 n_vertex = vector_vertex_node.size();
42872
42873 // Now update the polyline according to the new vertices
42874 // The new one representation
42875 TriangleMeshPolyLine* tmp_polyline_pt =
42876 new TriangleMeshPolyLine(vector_vertex_node, bound);
42877
42878 // Create a temporal "curve section" version of the recently created
42879 // polyline
42880 TriangleMeshCurveSection* tmp_curve_section_pt = tmp_polyline_pt;
42881
42882 // Tolerance below which the middle point can be deleted
42883 // (ratio of deflection to element length)
42884 double unrefinement_tolerance =
42885 open_curve_pt->polyline_pt(cs)->unrefinement_tolerance();
42886
42887 // Tolerance to add points
42888 double refinement_tolerance =
42889 open_curve_pt->polyline_pt(cs)->refinement_tolerance();
42890
42891 // Establish refinement and unrefinement tolerance
42892 tmp_polyline_pt->set_unrefinement_tolerance(unrefinement_tolerance);
42893 tmp_polyline_pt->set_refinement_tolerance(refinement_tolerance);
42894
42895 // Establish the maximum length constraint
42896 double maximum_length =
42897 open_curve_pt->polyline_pt(cs)->maximum_length();
42898 tmp_polyline_pt->set_maximum_length(maximum_length);
42899
42900 if (n_vertex >= 2)
42901 {
42902 // Pass the connection information from the old polyline to the
42903 // new one
42904 this->copy_connection_information(open_curve_pt->polyline_pt(cs),
42905 tmp_curve_section_pt);
42906 }
42907
42908 // Now update the polyline according to the new vertices but first
42909 // check if the object is allowed to delete the representation or
42910 // if it should be done by other object
42911 bool delete_it_on_destructor = false;
42912
42913 std::set<TriangleMeshCurveSection*>::iterator it =
42914 this->Free_curve_section_pt.find(open_curve_pt->curve_section_pt(cs));
42915
42916 if (it != this->Free_curve_section_pt.end())
42917 {
42918 this->Free_curve_section_pt.erase(it);
42919 delete open_curve_pt->curve_section_pt(cs);
42920 delete_it_on_destructor = true;
42921 }
42922
42923 // *****************************************************************
42924 // Copying the new representation
42925 open_curve_pt->curve_section_pt(cs) = tmp_polyline_pt;
42926
42927 // Update the Boundary - Polyline map
42928 this->Boundary_curve_section_pt[bound] =
42929 open_curve_pt->curve_section_pt(cs);
42930
42931 if (delete_it_on_destructor)
42932 {
42933 this->Free_curve_section_pt.insert(
42934 open_curve_pt->curve_section_pt(cs));
42935 }
42936
42937#ifdef OOMPH_HAS_MPI
42938
42939 // If there are not sub-boundaries mark the boundary if need to be
42940 // trated as shared or as internal boundary
42941 if (this->is_mesh_distributed() && nsub_boundaries == 1)
42942 {
42943 // Clear all previous stored data
42944 this->Boundary_marked_as_shared_boundary[bound].clear();
42945
42946 // .. and store the flag for the boundary
42947 this->Boundary_marked_as_shared_boundary[bound].push_back(
42948 internal_to_shared_boundary[0]);
42949 }
42950 // --------- Stuff for the sub_boundaries ----- Begin section --------
42951 // Verify if need to deal with sub_boundaries
42952 else if (this->is_mesh_distributed() && nsub_boundaries > 1)
42953 {
42954 // Create temporary representations for the boundaries, only to
42955 // create the mesh when calling Triangle
42956 // Clear all previous stored data
42957 this->Boundary_subpolylines[bound].clear();
42958 // Now create storage for the sub-boundaries
42959 this->Boundary_subpolylines[bound].resize(nsub_boundaries);
42960
42961 // Clear all previous stored data
42962 this->Boundary_marked_as_shared_boundary[bound].clear();
42963 // Create storage to mark the internal boundaries as shared
42964 // boundaries
42965 this->Boundary_marked_as_shared_boundary[bound].resize(
42966 nsub_boundaries);
42967 for (unsigned isub = 0; isub < nsub_boundaries; isub++)
42968 {
42969 // Now update the polyline according to the sub set of
42970 // vertices, set the chunk number of the polyline
42971 TriangleMeshPolyLine* sub_tmp_polyline_pt =
42973 sub_vector_vertex_node[isub], bound, isub);
42974
42975 // Add the sub-polyline to the container to represent the
42976 // boundary in parts
42977 this->Boundary_subpolylines[bound][isub] = sub_tmp_polyline_pt;
42978
42979 // Copy the flag that mark the boundary as internal or as
42980 // shared bound
42981 this->Boundary_marked_as_shared_boundary[bound][isub] =
42982 internal_to_shared_boundary[isub];
42983
42984 // No need to send the unrefinement/refinement and maximum
42985 // length constraints since these are only temporary
42986 // representations
42987 }
42988
42989 } // if (this->is_mesh_distributed() && nsub_boundaries > 1)
42990 // --------- Stuff for the sub_boundaries ----- End section ---------
42991#endif // OOMPH_HAS_MPI
42992
42993 } // update polyline representation
42994
42995 // Delete the allocated memory for the geometric object
42996 // that represents the curvilinear boundary
42997 delete mesh_geom_obj_pt;
42998
42999 } // npolyline
43000
43001 // Cleanup the face mesh
43002 for (unsigned p = 0; p < ncurve_section; p++)
43003 {
43004 face_mesh_pt[p]->flush_node_storage();
43005 delete face_mesh_pt[p];
43006 }
43007 }
43008
43009#ifdef OOMPH_HAS_MPI
43010 //======================================================================
43011 /// Updates the shared polylines representation after restart
43012 //======================================================================
43013 template<class ELEMENT>
43015 Vector<TriangleMeshPolyLine*>& vector_polyline_pt)
43016 {
43017 // Go through all the shared boundaries/polylines
43018 const unsigned npolylines = vector_polyline_pt.size();
43019 for (unsigned pp = 0; pp < npolylines; pp++)
43020 {
43021 // Get the boundary of the current polyline
43022 const unsigned b = vector_polyline_pt[pp]->boundary_id();
43023
43024 // Get the edges of the shared boundary elements that create the
43025 // shared boundary and store the shared boundary elements from where
43026 // were created
43027 std::map<std::pair<Node*, Node*>, FiniteElement*> halo_edge_element_pt;
43028 std::map<std::pair<Node*, Node*>, FiniteElement*> nonhalo_edge_element_pt;
43029
43030 // Store the nodes that define the edges
43031 Vector<Node*> halo_edge_nodes_pt;
43032 Vector<Node*> nonhalo_edge_nodes_pt;
43033
43034 // Go through the shared boundary elements and store their edges
43035 const unsigned nshared_bound_ele = this->nshared_boundary_element(b);
43036 for (unsigned e = 0; e < nshared_bound_ele; e++)
43037 {
43038 // Get the shared boundary element
43039 FiniteElement* current_ele_pt = this->shared_boundary_element_pt(b, e);
43040
43041 // Get the corner nodes, the first three nodes
43042 Node* first_node_pt = current_ele_pt->node_pt(0);
43043 Node* second_node_pt = current_ele_pt->node_pt(1);
43044 Node* third_node_pt = current_ele_pt->node_pt(2);
43045
43046 // Check if the elements is halo
43047 if (!current_ele_pt->is_halo())
43048 {
43049 // Store the edges
43050 nonhalo_edge_nodes_pt.push_back(first_node_pt);
43051 nonhalo_edge_nodes_pt.push_back(second_node_pt);
43052
43053 nonhalo_edge_nodes_pt.push_back(second_node_pt);
43054 nonhalo_edge_nodes_pt.push_back(third_node_pt);
43055
43056 nonhalo_edge_nodes_pt.push_back(third_node_pt);
43057 nonhalo_edge_nodes_pt.push_back(first_node_pt);
43058
43059 // Store the info. of the element used to create these edges
43060 std::pair<Node*, Node*> edge1 =
43061 std::make_pair(first_node_pt, second_node_pt);
43062 nonhalo_edge_element_pt[edge1] = current_ele_pt;
43063
43064 std::pair<Node*, Node*> edge2 =
43065 std::make_pair(second_node_pt, third_node_pt);
43066 nonhalo_edge_element_pt[edge2] = current_ele_pt;
43067
43068 std::pair<Node*, Node*> edge3 =
43069 std::make_pair(third_node_pt, first_node_pt);
43070 nonhalo_edge_element_pt[edge3] = current_ele_pt;
43071 }
43072 else
43073 {
43074 // Store the edges
43075 halo_edge_nodes_pt.push_back(first_node_pt);
43076 halo_edge_nodes_pt.push_back(second_node_pt);
43077
43078 halo_edge_nodes_pt.push_back(second_node_pt);
43079 halo_edge_nodes_pt.push_back(third_node_pt);
43080
43081 halo_edge_nodes_pt.push_back(third_node_pt);
43082 halo_edge_nodes_pt.push_back(first_node_pt);
43083
43084 // Store the info. of the element used to create these edges
43085 std::pair<Node*, Node*> edge1 =
43086 std::make_pair(first_node_pt, second_node_pt);
43087 halo_edge_element_pt[edge1] = current_ele_pt;
43088
43089 std::pair<Node*, Node*> edge2 =
43090 std::make_pair(second_node_pt, third_node_pt);
43091 halo_edge_element_pt[edge2] = current_ele_pt;
43092
43093 std::pair<Node*, Node*> edge3 =
43094 std::make_pair(third_node_pt, first_node_pt);
43095 halo_edge_element_pt[edge3] = current_ele_pt;
43096 }
43097
43098 } // for (e < nshared_bound_ele)
43099
43100 // Filter the edges that give rise to a shared boundary
43101
43102 // Mark the done edges
43103 std::map<std::pair<Node*, Node*>, bool> edge_done;
43104
43105 // Storage for the edges shared by the elements
43106 Vector<std::pair<Node*, Node*>> unsorted_edges;
43107
43108 // Storage for the elements that created the unsorted edges (two
43109 // elements, one at each side of the shared boundary)
43110 Vector<Vector<FiniteElement*>> unsorted_edges_elements_pt;
43111
43112 const unsigned nnonhalo_edge_nodes = nonhalo_edge_nodes_pt.size();
43113 for (unsigned i = 0; i < nnonhalo_edge_nodes; i += 2)
43114 {
43115 Vector<Node*> currenti_edge(2);
43116 currenti_edge[0] = nonhalo_edge_nodes_pt[i];
43117 currenti_edge[1] = nonhalo_edge_nodes_pt[i + 1];
43118
43119 // Create the edge (both nodes that make the edge)
43120 std::pair<Node*, Node*> new_edge =
43121 std::make_pair(currenti_edge[0], currenti_edge[1]);
43122
43123 if (!edge_done[new_edge])
43124 {
43125 const unsigned nhalo_edge_nodes = halo_edge_nodes_pt.size();
43126 for (unsigned j = 0; j < nhalo_edge_nodes; j += 2)
43127 {
43128 Vector<Node*> currentj_edge(2);
43129 currentj_edge[0] = halo_edge_nodes_pt[j];
43130 currentj_edge[1] = halo_edge_nodes_pt[j + 1];
43131
43132 // Comparing pointer of nodes
43133 if (currenti_edge[0] == currentj_edge[0] &&
43134 currenti_edge[1] == currentj_edge[1])
43135 {
43136 // Store the edge in the proper container
43137 unsorted_edges.push_back(new_edge);
43138
43139 // Get the elements associated with the edges
43140 Vector<FiniteElement*> tmp_edge_element_pt;
43141
43142 FiniteElement* nonhalo_ele_pt = nonhalo_edge_element_pt[new_edge];
43143 FiniteElement* halo_ele_pt = halo_edge_element_pt[new_edge];
43144
43145 tmp_edge_element_pt.push_back(nonhalo_ele_pt);
43146 tmp_edge_element_pt.push_back(halo_ele_pt);
43147
43148 // Store the elements associated with the edge
43149 unsorted_edges_elements_pt.push_back(tmp_edge_element_pt);
43150
43151 // Mark the edge as done
43152 edge_done[new_edge] = true;
43153
43154 // Break the loop for (j < nedge_node)
43155 break;
43156
43157 } // equal edge
43158
43159 // Comparing pointer of nodes (reversed)
43160 else if (currenti_edge[0] == currentj_edge[1] &&
43161 currenti_edge[1] == currentj_edge[0])
43162 {
43163 // Create the edge (both nodes that make the edge)
43164 std::pair<Node*, Node*> new_edge =
43165 std::make_pair(currenti_edge[0], currenti_edge[1]);
43166
43167 // Store the edge in the proper container
43168 unsorted_edges.push_back(new_edge);
43169
43170 // Create the (reversed) edge (both nodes that make the edge)
43171 std::pair<Node*, Node*> rev_new_edge =
43172 std::make_pair(currentj_edge[0], currentj_edge[1]);
43173
43174 // Get the elements associated with the edge
43175 Vector<FiniteElement*> tmp_edge_element_pt;
43176
43177 FiniteElement* nonhalo_ele_pt = nonhalo_edge_element_pt[new_edge];
43178 FiniteElement* halo_ele_pt = halo_edge_element_pt[rev_new_edge];
43179
43180 tmp_edge_element_pt.push_back(nonhalo_ele_pt);
43181 tmp_edge_element_pt.push_back(halo_ele_pt);
43182
43183 // Store the elements associated with the edge
43184 unsorted_edges_elements_pt.push_back(tmp_edge_element_pt);
43185
43186 // Mark the edge as done
43187 edge_done[new_edge] = true;
43188
43189 // Break the loop for (j < nedge_node)
43190 break;
43191
43192 } // if (equal edge)
43193
43194 } // for (j < nhalo_edge_nodes)
43195
43196 } // if (!edge_done[new_edge])
43197
43198 } // for (i < nnonhalo_edge_nodes)
43199
43200 // We already have the edges that make the shared boundary (and the
43201 // elements)
43202 // Sort them to create a contiguous boundary
43203
43204 // Mark the already sorted edges
43205 std::map<std::pair<Node*, Node*>, bool> edge_sorted;
43206
43207 const unsigned nunsorted_edges = unsorted_edges.size();
43208
43209#ifdef PARANOID
43210 // The number of unsorted edges must be the same as the number of
43211 // shared_boundary element / 2
43212 if (nshared_bound_ele / 2 != nunsorted_edges)
43213 {
43214 std::ostringstream error_message;
43215 error_message
43216 << "The number of shared boundary elements (" << nshared_bound_ele
43217 << ") is not the double\nof the number of unsorted edges ("
43218 << nunsorted_edges << ") for the current boundary (" << b << ")\n\n";
43219 throw OomphLibError(
43220 error_message.str(),
43221 "RefineableTriangleMesh::update_shared_curve_after_restart()",
43222 OOMPH_EXCEPTION_LOCATION);
43223 }
43224#endif
43225
43226 unsigned nsorted_edges = 0;
43227
43228 // Storing for the sorting nodes extracted from the edges, and
43229 // then used to update the polyline
43230 std::list<Node*> sorted_nodes;
43231
43232 // Storing for the edges elements
43233 std::list<FiniteElement*> sorted_edges_elements_pt;
43234
43235 // Get the root edge
43236 std::pair<Node*, Node*> edge = unsorted_edges[0];
43237 nsorted_edges++;
43238
43239 // Mark edge as done
43240 edge_sorted[edge] = true;
43241
43242 // The initial and final node on the list
43243 Node* first_node_pt = edge.first;
43244 Node* last_node_pt = edge.second;
43245
43246 // Push back on the list the new edge (nodes)
43247 sorted_nodes.push_back(first_node_pt);
43248 sorted_nodes.push_back(last_node_pt);
43249
43250 // Store the elements for the current edge
43251 sorted_edges_elements_pt.push_back(unsorted_edges_elements_pt[0][0]);
43252 sorted_edges_elements_pt.push_back(unsorted_edges_elements_pt[0][1]);
43253
43254 // Iterate while the number of sorted edges be less than the number of
43255 // unsorted edges
43256 while (nsorted_edges < nunsorted_edges)
43257 {
43258 // Flag to indicate when a node was added
43259 bool node_added = false;
43260
43261 // Start from the next edge since we have already added the
43262 // previous one as the initial edge
43263 for (unsigned iedge = 1; iedge < nunsorted_edges; iedge++)
43264 {
43265 edge = unsorted_edges[iedge];
43266
43267 // If edge not done
43268 if (!edge_sorted[edge])
43269 {
43270 // Get each individual node
43271 Node* left_node_pt = edge.first;
43272 Node* right_node_pt = edge.second;
43273
43274 if (left_node_pt == first_node_pt)
43275 {
43276 // Push front the new node
43277 sorted_nodes.push_front(right_node_pt);
43278 first_node_pt = right_node_pt;
43279 node_added = true;
43280
43281 // Store the elements for the current edge
43282 sorted_edges_elements_pt.push_front(
43283 unsorted_edges_elements_pt[iedge][1]);
43284 sorted_edges_elements_pt.push_front(
43285 unsorted_edges_elements_pt[iedge][0]);
43286 }
43287 else if (left_node_pt == last_node_pt)
43288 {
43289 // Push back the new node
43290 sorted_nodes.push_back(right_node_pt);
43291 last_node_pt = right_node_pt;
43292 node_added = true;
43293
43294 // Store the elements for the current edge
43295 sorted_edges_elements_pt.push_back(
43296 unsorted_edges_elements_pt[iedge][0]);
43297 sorted_edges_elements_pt.push_back(
43298 unsorted_edges_elements_pt[iedge][1]);
43299 }
43300 else if (right_node_pt == first_node_pt)
43301 {
43302 // Push front the new node
43303 sorted_nodes.push_front(left_node_pt);
43304 first_node_pt = left_node_pt;
43305 node_added = true;
43306
43307 // Store the elements for the current edge
43308 sorted_edges_elements_pt.push_front(
43309 unsorted_edges_elements_pt[iedge][1]);
43310 sorted_edges_elements_pt.push_front(
43311 unsorted_edges_elements_pt[iedge][0]);
43312 }
43313 else if (right_node_pt == last_node_pt)
43314 {
43315 // Push back the new node
43316 sorted_nodes.push_back(left_node_pt);
43317 last_node_pt = left_node_pt;
43318 node_added = true;
43319
43320 // Store the elements for the current edge
43321 sorted_edges_elements_pt.push_back(
43322 unsorted_edges_elements_pt[iedge][0]);
43323 sorted_edges_elements_pt.push_back(
43324 unsorted_edges_elements_pt[iedge][1]);
43325 }
43326
43327 if (node_added)
43328 {
43329 // Mark as done only if one of its nodes has been
43330 // added to the list
43331 edge_sorted[edge] = true;
43332 nsorted_edges++;
43333
43334 // Break the for
43335 break;
43336 }
43337
43338 } // if (!edge_done[edge])
43339 } // for (iedge < nunsorted_edges)
43340 } // while (nsorted_edges < nunsorted_edges)
43341
43342 // At this point we already have a sorted list of nodes, get the
43343 // vertices from them and store them in a vector container
43344
43345 // Get the number of nodes on the list
43346 unsigned nvertex = sorted_nodes.size();
43347 // The vector to store the vertices (assign space)
43348 Vector<Vector<double>> polyline_vertices(nvertex);
43349
43350 // Copy the vertices of the nodes
43351 unsigned counter = 0;
43352 for (std::list<Node*>::iterator it_nodes = sorted_nodes.begin();
43353 it_nodes != sorted_nodes.end();
43354 it_nodes++)
43355 {
43356 polyline_vertices[counter].resize(2);
43357 polyline_vertices[counter][0] = (*it_nodes)->x(0);
43358 polyline_vertices[counter][1] = (*it_nodes)->x(1);
43359 counter++;
43360 }
43361
43362 // Before going to the unrefinement or refinement process check that
43363 // all processors start from the same vertex. Start from the bottom
43364 // left vertex
43365 if (polyline_vertices[nvertex - 1][1] < polyline_vertices[0][1])
43366 {
43367 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43368 }
43369 else if (polyline_vertices[nvertex - 1][1] == polyline_vertices[0][1])
43370 {
43371 if (polyline_vertices[nvertex - 1][0] < polyline_vertices[0][0])
43372 {
43373 std::reverse(polyline_vertices.begin(), polyline_vertices.end());
43374 }
43375 }
43376
43377 // Create the polyline associated with this edge
43378 TriangleMeshPolyLine* new_polyline_pt =
43379 new TriangleMeshPolyLine(polyline_vertices, b);
43380
43381 // Get the curve section representation
43382 TriangleMeshCurveSection* curve_section_pt = vector_polyline_pt[pp];
43383
43384 // Copy the connection information from the old shared polyline
43385 // to the new one
43386 this->copy_connection_information(curve_section_pt, new_polyline_pt);
43387
43388 // Now update the polyline according to the new vertices but first
43389 // check if the object is allowed to delete the representation
43390 // or if it should be done by other object
43391 bool delete_it_on_destructor = false;
43392
43393 // Establish the element as being deleted by the destructor of
43394 // the class
43395 std::set<TriangleMeshCurveSection*>::iterator it =
43396 this->Free_curve_section_pt.find(curve_section_pt);
43397
43398 if (it != this->Free_curve_section_pt.end())
43399 {
43400 this->Free_curve_section_pt.erase(it);
43401 delete curve_section_pt;
43402 delete_it_on_destructor = true;
43403 }
43404
43405 // Copy the new representation
43406 vector_polyline_pt[pp] = new_polyline_pt;
43407
43408 // Get the new curve section representation
43409 TriangleMeshCurveSection* new_curve_section_pt = vector_polyline_pt[pp];
43410
43411 // Update the Boundary - Polyline map
43412 this->Boundary_curve_section_pt[b] = new_curve_section_pt;
43413
43414 if (delete_it_on_destructor)
43415 {
43416 this->Free_curve_section_pt.insert(new_curve_section_pt);
43417 }
43418
43419 } // for (pp < npoly)
43420 }
43421
43422 //===================================================================
43423 // Fill the boundary elements structures when dealing with
43424 // shared boundaries that overlap internal boundaries. Document the
43425 // number of elements on the shared boundaries that go to internal
43426 // boundaries
43427 //===================================================================
43428 template<class ELEMENT>
43430 ELEMENT>::fill_boundary_elements_and_nodes_for_internal_boundaries()
43431 {
43432 // Dummy file
43433 std::ofstream some_file;
43434 fill_boundary_elements_and_nodes_for_internal_boundaries(some_file);
43435 }
43436
43437 //===================================================================
43438 // Fill the boundary elements structures when dealing with
43439 // shared boundaries that overlap internal boundaries
43440 //===================================================================
43441 template<class ELEMENT>
43444 std::ofstream& outfile)
43445 {
43446 // Get the number of processors
43447 const unsigned nproc = this->communicator_pt()->nproc();
43448 // Get the rank of the current processor
43449 unsigned my_rank = this->communicator_pt()->my_rank();
43450
43451 // Temporal name for the shared boundary overlaps structure
43452 std::map<unsigned, unsigned> shd_bnd_over_int_bnd =
43453 this->Shared_boundary_overlaps_internal_boundary;
43454
43455 // Register the internal boundary elements that where found to be
43456 // overlapped by shared boundaries
43457 std::set<unsigned> internal_boundary_overlaped;
43458
43459 // Document the number of elements and nodes associated to the
43460 // boundaries before filling elements and nodes
43461 if (outfile.is_open())
43462 {
43463 const unsigned nbound = this->nboundary();
43464 outfile << "Number of boundaries: " << nbound << "\n\n";
43465 outfile << "Number of elements and nodes associated to each "
43466 << "boundary before\nfilling elements and nodes\n\n";
43467 for (unsigned i = 0; i < nbound; i++)
43468 {
43469 outfile << "Boundary (" << i << ") Elements ("
43470 << this->nboundary_element(i) << ") "
43471 << "Nodes (" << this->nboundary_node(i) << ")\n";
43472 }
43473 }
43474
43475 // Storage for the shared boundaries in this processor
43476 std::set<unsigned> shared_boundaries_in_this_processor;
43477
43478 // Get the shared boundaries that this processor has with other
43479 // processors
43480 for (unsigned iproc = 0; iproc < nproc; iproc++)
43481 {
43482 // Work with other processors only
43483 if (iproc != my_rank)
43484 {
43485 // Get the number of boundaries shared with the "iproc"-th processor
43486 unsigned nshared_boundaries_with_iproc =
43487 this->nshared_boundaries(my_rank, iproc);
43488
43489 if (nshared_boundaries_with_iproc > 0)
43490 {
43491 // Get the boundaries ids shared with "iproc"-th processor
43492 Vector<unsigned> bound_shared_with_iproc;
43493 bound_shared_with_iproc = this->shared_boundaries_ids(my_rank, iproc);
43494
43495 // Loop over shared boundaries with "iproc"-th processor
43496 for (unsigned bs = 0; bs < nshared_boundaries_with_iproc; bs++)
43497 {
43498 unsigned bnd_id = bound_shared_with_iproc[bs];
43499 shared_boundaries_in_this_processor.insert(bnd_id);
43500 }
43501 }
43502 }
43503 }
43504
43505 // ------------------------------------------------------------------
43506 // Copy the boundary elements and nodes from the shared boundary to
43507 // the internal boundary it overlaps
43508 // ------------------------------------------------------------------
43509 // Go through the shared boundaries that overlap internal boundaries
43510 for (std::map<unsigned, unsigned>::iterator it =
43511 shd_bnd_over_int_bnd.begin();
43512 it != shd_bnd_over_int_bnd.end();
43513 it++)
43514 {
43515 // The shared boundary id that overlaps with an internal boundary
43516 const unsigned shd_bnd_id = (*it).first;
43517 // The internal boundary overlapped by the shared boundary
43518 const unsigned int_bnd_id = (*it).second;
43519
43520 // Check if the shared boundary exist in this processor
43521 std::set<unsigned>::iterator it_set =
43522 shared_boundaries_in_this_processor.find(shd_bnd_id);
43523 if (it_set != shared_boundaries_in_this_processor.end())
43524 {
43525 internal_boundary_overlaped.insert(int_bnd_id);
43526
43527 // -----------------------------------------------------------------
43528 // First work the nodes of the shared boundaries that should be
43529 // added to the internal boundaries
43530 const unsigned nbnd_node_shd_bnd = this->nboundary_node(shd_bnd_id);
43531
43532 // Document the number of nodes that will be passed to the internal
43533 // boundary from the current shared boundary
43534 if (outfile.is_open())
43535 {
43536 outfile << "\nPass info. from shared (" << shd_bnd_id
43537 << ") to internal (" << int_bnd_id << ")\n";
43538 outfile << "Number of shared boundary nodes: " << nbnd_node_shd_bnd
43539 << "\n";
43540 }
43541
43542 for (unsigned in = 0; in < nbnd_node_shd_bnd; in++)
43543 {
43544 // Get the boundary node
43545 Node* bnd_node_pt = this->boundary_node_pt(shd_bnd_id, in);
43546 // Add the node to the internal boundary
43547 this->add_boundary_node(int_bnd_id, bnd_node_pt);
43548 }
43549
43550 // -----------------------------------------------------------------
43551 // Second work the boundary elements
43552 // Get the number of boundary elements that should be copied to the
43553 // internal boundary
43554 const unsigned nbnd_ele_shd_bnd = this->nboundary_element(shd_bnd_id);
43555
43556 // Document the number of elements that will be passed to the
43557 // internal boundary from the current shared boundary
43558 if (outfile.is_open())
43559 {
43560 outfile << "Number of shared boundary elements: " << nbnd_ele_shd_bnd
43561 << "\n\n";
43562 }
43563
43564 // Go through the boundary elements in the shrared boundary and add
43565 // them to the boundary elements of the internal boundary
43566 for (unsigned ie = 0; ie < nbnd_ele_shd_bnd; ie++)
43567 {
43568 // Get the boundary element
43569 FiniteElement* bnd_ele_pt = this->boundary_element_pt(shd_bnd_id, ie);
43570 // Add the element to the boundary elements storage of the
43571 // internal boundary
43572 Boundary_element_pt[int_bnd_id].push_back(bnd_ele_pt);
43573 // Get the face index of the boundary
43574 int face_index = this->face_index_at_boundary(shd_bnd_id, ie);
43575 // Add the face index to the storage of the boundary
43576 Face_index_at_boundary[int_bnd_id].push_back(face_index);
43577
43578 } // for (ie < nbnd_ele_shd_bnd)
43579
43580 // If there are regions we need to fill the storage for regions too
43581 const unsigned nregions = this->nregion();
43582 if (nregions > 1)
43583 {
43584 for (unsigned ir = 0; ir < nregions; ir++)
43585 {
43586 // Get the region attribute
43587 const unsigned region_id =
43588 static_cast<unsigned>(this->Region_attribute[ir]);
43589
43590 // Loop over all elements on boundaries in region ir
43591 const unsigned nele_ir =
43592 this->nboundary_element_in_region(shd_bnd_id, region_id);
43593 for (unsigned ier = 0; ier < nele_ir; ier++)
43594 {
43595 // Get the boundary element in current region
43596 FiniteElement* bnd_ele_pt =
43597 this->boundary_element_in_region_pt(shd_bnd_id, region_id, ier);
43598 // Add the boundary element to the internal boundary in the
43599 // region
43600 this->Boundary_region_element_pt[int_bnd_id][region_id].push_back(
43601 bnd_ele_pt);
43602
43603 // Get the face index of the boundary
43604 int face_index = this->face_index_at_boundary_in_region(
43605 shd_bnd_id, region_id, ier);
43606 // Add the face index to the storage of the boundary region
43607 this->Face_index_region_at_boundary[int_bnd_id][region_id]
43608 .push_back(face_index);
43609
43610 } // for (ier < nele_ir)
43611
43612 } // for (ir < nregions)
43613
43614 } // if (nregions > 1)
43615
43616 } // if (the shared boundary appears in the current processor)
43617
43618 } // for (loop over the shared bound that overlap an internal bound)
43619
43620 // Document the number of elements and nodes associated to the
43621 // boundaries after filling elements and nodes
43622 if (outfile.is_open())
43623 {
43624 const unsigned nbound = this->nboundary();
43625 outfile << "Number of boundaries: " << nbound << "\n\n";
43626 outfile << "Number of elements and nodes associated to each "
43627 << "boundary after\nfilling elements and nodes\n\n";
43628 for (unsigned i = 0; i < nbound; i++)
43629 {
43630 outfile << "Boundary (" << i << ") Elements ("
43631 << this->nboundary_element(i) << ")"
43632 << " Nodes (" << this->nboundary_node(i) << ")\n";
43633 }
43634 }
43635
43636 // ------------------------------------------------------------------
43637 // Finally, re-setup the boundary coordinates for the new nodes on
43638 // the overlaped internal boundaries
43639 // ------------------------------------------------------------------
43640 for (std::set<unsigned>::iterator it = internal_boundary_overlaped.begin();
43641 it != internal_boundary_overlaped.end();
43642 it++)
43643 {
43644 const unsigned overlaped_internal_bnd_id = (*it);
43645
43646 // Re-setup boundary coordinates
43647 this->template setup_boundary_coordinates<ELEMENT>(
43648 overlaped_internal_bnd_id);
43649 }
43650 }
43651
43652#endif // #ifdef OOMPH_HAS_MPI
43653
43654 //======================================================================
43655 /// Move the boundary nodes onto the boundary defined by the old mesh
43656 //======================================================================
43657 template<class ELEMENT>
43659 RefineableTriangleMesh<ELEMENT>*& new_mesh_pt, const unsigned& b)
43660 {
43661 // Quick return
43662 if (!Boundary_coordinate_exists[b])
43663 {
43664 return;
43665 }
43666
43667 // Firstly we set the boundary coordinates of the new nodes
43668 // In case the mapping between the geometric object's intrinsic coordinate
43669 // and the arc-length coordinate is nonlinear. This is only an
43670 // approximation, but it will ensure that the nodes that were input to
43671 // triangle will retain exactly the same boundary coordinates and then
43672 // linear interpolation is used between those values for any newly created
43673 // nodes.
43674
43675 // We need to get the boundary nodes from the boundary face
43676 // elements since the "multi_domain" methods add nodes to the
43677 // "Boundary_node_pt" structure which have no boundary coordinates
43678 // assigned
43679 std::set<Node*> tmp_boundary_node_pt;
43680 const unsigned nboundary_ele = this->nboundary_element(b);
43681 for (unsigned e = 0; e < nboundary_ele; e++)
43682 {
43683 // Get the boundary bulk element
43684 FiniteElement* bulk_ele_pt = this->boundary_element_pt(b, e);
43685#ifdef OOMPH_HAS_MPI
43686 // Only work with nonhalo elements if the mesh is distributed
43687 if (!bulk_ele_pt->is_halo())
43688 {
43689#endif
43690 // Get the face index
43691 int face_index = this->face_index_at_boundary(b, e);
43692 // Create the face element
43693 FiniteElement* face_ele_pt =
43694 new DummyFaceElement<ELEMENT>(bulk_ele_pt, face_index);
43695
43696 // Get the number of nodes on the face element
43697 const unsigned nnodes = face_ele_pt->nnode();
43698 for (unsigned i = 0; i < nnodes; i++)
43699 {
43700 // Get the nodes in the face elements
43701 Node* tmp_node_pt = face_ele_pt->node_pt(i);
43702 // Add the nodes to the set of boundary nodes
43703 tmp_boundary_node_pt.insert(tmp_node_pt);
43704 } // for (i < nnodes)
43705
43706 // Free the memory allocated for the face element
43707 delete face_ele_pt;
43708 face_ele_pt = 0;
43709#ifdef OOMPH_HAS_MPI
43710 } // if (!bulk_ele_pt->is_halo())
43711#endif
43712
43713 } // for (e < nboundary_ele)
43714
43715 // Get the number of boundary nodes
43716 const unsigned long n_boundary_node = tmp_boundary_node_pt.size();
43717
43718 // Quick return if there are no nodes
43719 if (n_boundary_node == 0)
43720 {
43721#ifdef OOMPH_HAS_MPI
43722 // Check if we are working with a distributed mesh
43723 if (!this->is_mesh_distributed())
43724 {
43725#endif
43726 return;
43727#ifdef OOMPH_HAS_MPI
43728 }
43729 else // The mesh is distributed !!!
43730 {
43731 // Do not forget to participate in the communication
43732 Mesh* face_mesh_pt = new Mesh();
43733 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43734 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
43735
43736 // Delete the allocated memory for the geometric object and face mesh
43737 delete mesh_geom_obj_pt;
43738
43739 // Flush the nodes from the face mesh to make sure we
43740 // don't delete them (the bulk mesh still needs them!)
43741 face_mesh_pt->flush_node_storage();
43742 delete face_mesh_pt;
43743 return;
43744 }
43745#endif
43746 } // if (n_boundary_node==0)
43747
43748 // Create a vector of existing boundary nodes with their boundary
43749 // coordinate as the first entry so that we can use standard sort algorithms
43750 Vector<double> node_coord(3);
43751 Vector<double> b_coord(1);
43752
43753 Vector<Vector<double>> old_boundary_node(n_boundary_node);
43754 unsigned tmp_counter = 0;
43755 for (std::set<Node*>::iterator it_node = tmp_boundary_node_pt.begin();
43756 it_node != tmp_boundary_node_pt.end();
43757 it_node++, tmp_counter++)
43758 {
43759 Node* nod_pt = (*it_node);
43760 nod_pt->get_coordinates_on_boundary(b, b_coord);
43761 node_coord[0] = b_coord[0];
43762 node_coord[1] = nod_pt->x(0);
43763 node_coord[2] = nod_pt->x(1);
43764 old_boundary_node[tmp_counter] = node_coord;
43765 } // for (it_node != tmp_boundary_node_pt.end())
43766
43767 // Sort the vector
43768 std::sort(old_boundary_node.begin(), old_boundary_node.end());
43769
43770 // Set up an equivalent ordered vector for the new nodes, based on the
43771 // current coordinate which is the scaled arc-length.
43772 // Also provide storage for the original node index,
43773 // the mapped coordinate and a flag to indicate whether the mapped
43774 // coordinate has been assigned.
43775 // Get the nodes on the boundary but consider to which segment (which
43776 // may appear in a distributed mesh) they belong
43777 Vector<Vector<Node*>> segment_nodes_pt;
43778
43779#ifdef OOMPH_HAS_MPI
43780 // Get the number of segments
43781 const unsigned nsegments = new_mesh_pt->nboundary_segment(b);
43782#else
43783 // The number of segments is one since the boundary is not split
43784 // over multiple processors
43785 const unsigned nsegments = 1;
43786#endif // #ifdef OOMPH_HAS_MPI
43787
43788#ifdef OOMPH_HAS_MPI
43789 // Get the total number of nodes on the boundary
43790 const unsigned n_new_boundary_node = new_mesh_pt->nboundary_segment_node(b);
43791
43792 // Check if we are working with a distributed mesh
43793 if (this->is_mesh_distributed())
43794 {
43795 // If that is the case we need to ensure that the new mesh has
43796 // nodes too, if that is not the case then return
43797 // Quick return if there are no nodes
43798 if (n_new_boundary_node == 0)
43799 {
43800 // Do not forget to participate in the communication
43801 Mesh* face_mesh_pt = new Mesh();
43802 create_unsorted_face_mesh_representation(b, face_mesh_pt);
43803 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
43804
43805 // Delete the allocated memory for the geometric object and face mesh
43806 delete mesh_geom_obj_pt;
43807 // Flush the nodes from the face mesh to make sure we
43808 // don't delete them (the bulk mesh still needs them!)
43809 face_mesh_pt->flush_node_storage();
43810 delete face_mesh_pt;
43811 return;
43812 }
43813 }
43814#endif // #ifdef OOMPH_HAS_MPI
43815
43816 // Create a vector of boundary nodes that must be moved
43817 Vector<Vector<unsigned>> nodes_to_be_snapped(nsegments);
43818
43819 // Go through all the segments to assign the snapped zeta coordinates
43820 // for the new nodes
43821 for (unsigned is = 0; is < nsegments; is++)
43822 {
43823#ifdef OOMPH_HAS_MPI
43824 const unsigned n_new_boundary_segment_node =
43825 new_mesh_pt->nboundary_segment_node(b, is);
43826#else
43827 const unsigned n_new_boundary_segment_node =
43828 new_mesh_pt->nboundary_node(b);
43829#endif // #ifdef OOMPH_HAS_MPI
43830
43831 Vector<Vector<double>> new_boundary_node(n_new_boundary_segment_node);
43832 // There will be six data associated with each node
43833 node_coord.resize(6, 0.0);
43834 for (unsigned n = 0; n < n_new_boundary_segment_node; n++)
43835 {
43836#ifdef OOMPH_HAS_MPI
43837 Node* nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
43838#else
43839 Node* nod_pt = new_mesh_pt->boundary_node_pt(b, n);
43840#endif // #ifdef OOMPH_HAS_MPI
43841 nod_pt->get_coordinates_on_boundary(b, b_coord);
43842 node_coord[0] = b_coord[0];
43843 node_coord[1] = nod_pt->x(0);
43844 node_coord[2] = nod_pt->x(1);
43845 node_coord[3] = n;
43846 new_boundary_node[n] = node_coord;
43847 } // for (n < n_new_boundary_segment_node)
43848
43849 // Sort the new boundary nodes based on their arc-length coordinate
43850 std::sort(new_boundary_node.begin(), new_boundary_node.end());
43851
43852 // We now have two sets of nodes ordered by a coordinate that acts in the
43853 // same direction and has the same limits.
43854
43855 // Loop over the vector of new nodes and allocate exactly the same
43856 // coordinate as the old nodes at points of coincidence
43857 unsigned old_index = 0;
43858 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43859 {
43860 // Loop over the set of old nodes and if the x and y coordinates
43861 // coincide with the new node copy accross the new boundary coordinate
43862 for (unsigned m = old_index; m < n_boundary_node; ++m)
43863 {
43864 if ((std::fabs(old_boundary_node[m][1] - new_boundary_node[n][1]) <
43865 1.0e-14) &&
43866 (std::fabs(old_boundary_node[m][2] - new_boundary_node[n][2]) <
43867 1.0e-14))
43868 {
43869 // Store the boundary coordinate from the old mesh
43870 new_boundary_node[n][4] = old_boundary_node[m][0];
43871 // Say that it has been stored
43872 new_boundary_node[n][5] = 1.0;
43873 // For efficiency, we can start the iteration from here next
43874 // time round because both vectors are ordered
43875 old_index = m;
43876 break;
43877 }
43878 }
43879 }
43880
43881 // Check that the end-points have new boundary coordinates allocated
43882#ifdef PARANOID
43883 if ((new_boundary_node[0][5] == 0.0) ||
43884 (new_boundary_node[n_new_boundary_segment_node - 1][5] == 0.0))
43885 {
43886 std::ostringstream error_stream;
43887 error_stream
43888 << "New boundary coordinates not found for the first and/or last "
43889 << "nodes\n"
43890 << "on the boundary " << b << ". This should not happen because "
43891 << "these\nlimits should have been setup in the constructor\n";
43892 error_stream
43893 << "The distance between the new and old nodes is probably outside\n"
43894 << "our tolerance.\n";
43895 error_stream.precision(20);
43896 error_stream << "Old boundaries: \n";
43897 error_stream << old_boundary_node[0][1] << " "
43898 << old_boundary_node[0][2] << " : "
43899 << old_boundary_node[n_boundary_node - 1][1] << " "
43900 << old_boundary_node[n_boundary_node - 1][2] << "\n";
43901 error_stream << "New boundaries: \n"
43902 << new_boundary_node[0][1] << " "
43903 << new_boundary_node[0][2] << " : "
43904 << new_boundary_node[n_new_boundary_segment_node - 1][1]
43905 << " "
43906 << new_boundary_node[n_new_boundary_segment_node - 1][2]
43907 << "\n";
43908 OomphLibWarning(error_stream.str(),
43909 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43910 OOMPH_EXCEPTION_LOCATION);
43911 }
43912#endif
43913
43914 // This is only true if the boundary is not splitted among the
43915 // processors
43916 if (!this->is_mesh_distributed())
43917 {
43918 // The end points should always be present, so we
43919 // can (and must) always add them in exactly
43920 new_boundary_node[0][4] = new_boundary_node[0][0];
43921
43922 /// Correct!? Because assigned again below
43923 new_boundary_node[n_new_boundary_segment_node - 1][4] =
43924 new_boundary_node[0][5] = 1.0;
43925
43926 new_boundary_node[n_new_boundary_segment_node - 1][4] =
43927 new_boundary_node[n_new_boundary_segment_node - 1][0];
43928 new_boundary_node[n_new_boundary_segment_node - 1][5] = 1.0;
43929 }
43930
43931 // Now loop over the interior nodes again and
43932 // use linear interpolation to fill in any unassigned coordiantes
43933 for (unsigned n = 1; n < n_new_boundary_segment_node - 1; ++n)
43934 {
43935 // If the new boundary coordinate has NOT been allocated
43936 if (new_boundary_node[n][5] == 0.0)
43937 {
43938 // Add its (unsorted) node number to the list
43939 nodes_to_be_snapped[is].push_back(
43940 static_cast<unsigned>(new_boundary_node[n][3]));
43941
43942 // We assume that the previous nodal value has been assigned
43943 // and read out the old and new boundary coordinates
43944 double zeta_old_low = new_boundary_node[n - 1][0];
43945 double zeta_new_low = new_boundary_node[n - 1][4];
43946
43947 // Loop over the nodes above the current node until
43948 // we find the next one that has been allocated
43949 for (unsigned m = n + 1; m < n_new_boundary_segment_node; ++m)
43950 {
43951 if (new_boundary_node[m][5] == 1.0)
43952 {
43953 // Read out the old boundary coordinate
43954 double zeta_old_high = new_boundary_node[m][0];
43955 double zeta_new_high = new_boundary_node[m][4];
43956 // Use linear interpolation to assign the new boundary coordinate
43957 double frac = (new_boundary_node[n][0] - zeta_old_low) /
43958 (zeta_old_high - zeta_old_low);
43959 new_boundary_node[n][4] =
43960 zeta_new_low + frac * (zeta_new_high - zeta_new_low);
43961 new_boundary_node[n][5] = 1.0;
43962 break;
43963 }
43964 }
43965 }
43966 }
43967
43968 // Loop over all the nodes and set the new boundary coordinate
43969 for (unsigned n = 0; n < n_new_boundary_segment_node; ++n)
43970 {
43971 if (new_boundary_node[n][5] == 0)
43972 {
43973 throw OomphLibError(
43974 "New boundary coordinate not assigned\n",
43975 "RefineableTriangleMesh::snap_nodes_onto_boundary()",
43976 OOMPH_EXCEPTION_LOCATION);
43977 }
43978
43979#ifdef OOMPH_HAS_MPI
43980 // get the old coordinate
43981 new_mesh_pt
43983 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43984 ->get_coordinates_on_boundary(b, b_coord);
43985 // Set the new coordinate
43986 b_coord[0] = new_boundary_node[n][4];
43987 new_mesh_pt
43989 b, is, static_cast<unsigned>(new_boundary_node[n][3]))
43990 ->set_coordinates_on_boundary(b, b_coord);
43991#else
43992 // get the old coordinate
43993 new_mesh_pt
43994 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
43995 ->get_coordinates_on_boundary(b, b_coord);
43996 // Set the new coordinate
43997 b_coord[0] = new_boundary_node[n][4];
43998 new_mesh_pt
43999 ->boundary_node_pt(b, static_cast<unsigned>(new_boundary_node[n][3]))
44000 ->set_coordinates_on_boundary(b, b_coord);
44001#endif // #ifdef OOMPH_HAS_MPI
44002 }
44003
44004 } // for (is < nsegments)
44005
44006 Mesh* face_mesh_pt = new Mesh();
44007 create_unsorted_face_mesh_representation(b, face_mesh_pt);
44008
44009 // Now that the coordinates have been set up we can do the snapping
44010 MeshAsGeomObject* mesh_geom_obj_pt = new MeshAsGeomObject(face_mesh_pt);
44011
44012 // Now assign the new nodes positions based on the old meshes
44013 // potentially curvilinear boundary (its geom object incarnation)
44014 Vector<double> new_x(2);
44015
44016 // Loop over the nodes that need to be snapped
44017 for (unsigned is = 0; is < nsegments; is++)
44018 {
44019 const unsigned nnodes_to_snap = nodes_to_be_snapped[is].size();
44020
44021 for (unsigned in = 0; in < nnodes_to_snap; in++)
44022 {
44023 // Read out the boundary node number
44024 unsigned n = nodes_to_be_snapped[is][in];
44025#ifdef OOMPH_HAS_MPI
44026 // Get the boundary coordinate of all new nodes
44027 Node* const nod_pt = new_mesh_pt->boundary_segment_node_pt(b, is, n);
44028#else
44029 // Get the boundary coordinate of all new nodes
44030 Node* const nod_pt = new_mesh_pt->boundary_node_pt(b, n);
44031#endif // #ifdef OOMPH_HAS_MPI
44032
44033 nod_pt->get_coordinates_on_boundary(b, b_coord);
44034 // Let's find boundary coordinates of the new node
44035 mesh_geom_obj_pt->position(b_coord, new_x);
44036
44037 // Now snap to the boundary
44038 for (unsigned i = 0; i < 2; i++)
44039 {
44040 nod_pt->x(i) = new_x[i];
44041 }
44042 }
44043 }
44044
44045 // Delete the allocated memory for the geometric object and face mesh
44046 delete mesh_geom_obj_pt;
44047 // Flush the nodes from the face mesh to make sure we
44048 // don't delete them (the bulk mesh still needs them!)
44049 face_mesh_pt->flush_node_storage();
44050 delete face_mesh_pt;
44051
44052 // Fix up the elements adjacent to the boundary
44053
44054 // Dummy six node element for sorting out bubble node for
44055 // seven node enriched quadratic triangles
44056 TElement<2, 3> dummy_six_node_element;
44057 for (unsigned j = 0; j < 6; j++)
44058 {
44059 dummy_six_node_element.construct_node(j);
44060 }
44061
44062 // This should definitely become a triangular element member function
44063 // Loop over elements
44064 unsigned n_bound_el = new_mesh_pt->nboundary_element(b);
44065 for (unsigned e = 0; e < n_bound_el; e++)
44066 {
44067 FiniteElement* el_pt = new_mesh_pt->boundary_element_pt(b, e);
44068
44069 // Deal with different numbers of nodes separately
44070 unsigned nnod = el_pt->nnode();
44071
44072 // #ifdef PARANOID
44073 // // Flag to indicate if we successully classified/dealt with the
44074 // element bool success=false;
44075 // #endif
44076
44077 // Simplex element: Nothing to be done other than error checking
44078 if (nnod == 3)
44079 {
44080#ifdef PARANOID
44081 // Try to cast to a simplex element
44082 TElement<2, 2>* t_el_pt = dynamic_cast<TElement<2, 2>*>(el_pt);
44083 if (t_el_pt == 0)
44084 {
44085 throw OomphLibError(
44086 "Have a three-noded element that's not a TElement<2,2>",
44087 OOMPH_CURRENT_FUNCTION,
44088 OOMPH_EXCEPTION_LOCATION);
44089 }
44090 // If I get there I must not have thrown :)
44091 // success=true;
44092#endif
44093 }
44094 // Quadratic element (or enriched quadratic)
44095
44096 else if ((nnod == 6) || (nnod == 7))
44097 {
44098#ifdef PARANOID
44099 // Try to cast to a quadratic element
44100 TElement<2, 3>* t_el_pt = dynamic_cast<TElement<2, 3>*>(el_pt);
44101 if (t_el_pt == 0)
44102 {
44103 if (nnod == 6)
44104 {
44105 throw OomphLibError(
44106 "Have a six-noded element that's not a TElement<2,3>",
44107 OOMPH_CURRENT_FUNCTION,
44108 OOMPH_EXCEPTION_LOCATION);
44109 }
44110 else
44111 {
44112 throw OomphLibError(
44113 "Have a seven-noded element that's not a TElement<2,3>",
44114 OOMPH_CURRENT_FUNCTION,
44115 OOMPH_EXCEPTION_LOCATION);
44116 }
44117 }
44118 // If I get there I must not have thrown :)
44119 // success=true;
44120#endif
44121 // Deal with six noded stuff for all (normal and enriched) elements
44122
44123 ///----------------------------------------------------------------
44124 /// Repositioning of mid-side nodes
44125 ///----------------------------------------------------------------
44126
44127 // Side between 0 and 1
44128 if (el_pt->node_pt(3)->is_on_boundary(b))
44129 {
44130 // Make sure that the node I'm about to move is NOT on
44131 // a boundary
44132 if (!el_pt->node_pt(5)->is_on_boundary())
44133 {
44134 // Reset the internal nodes
44135 for (unsigned i = 0; i < 2; i++)
44136 {
44137 el_pt->node_pt(5)->x(i) =
44138 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44139 }
44140 }
44141 // Make sure that the node I'm about to move is NOT on
44142 // a boundary
44143 if (!el_pt->node_pt(4)->is_on_boundary())
44144 {
44145 // Reset the internal nodes
44146 for (unsigned i = 0; i < 2; i++)
44147 {
44148 el_pt->node_pt(4)->x(i) =
44149 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44150 }
44151 }
44152 }
44153
44154 // Side between 1 and 2
44155 if (el_pt->node_pt(4)->is_on_boundary(b))
44156 {
44157 // Make sure that the node I'm about to move is NOT on
44158 // a boundary
44159 if (!el_pt->node_pt(5)->is_on_boundary())
44160 {
44161 // Reset the internal nodes
44162 for (unsigned i = 0; i < 2; i++)
44163 {
44164 el_pt->node_pt(5)->x(i) =
44165 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(2)->x(i));
44166 }
44167 }
44168 // Make sure that the node I'm about to move is NOT on
44169 // a boundary
44170 if (!el_pt->node_pt(3)->is_on_boundary())
44171 {
44172 // Reset the internal nodes
44173 for (unsigned i = 0; i < 2; i++)
44174 {
44175 el_pt->node_pt(3)->x(i) =
44176 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44177 }
44178 }
44179 }
44180
44181 // Side between 0 and 2
44182 if (el_pt->node_pt(5)->is_on_boundary(b))
44183 {
44184 // Make sure that the node I'm about to move is NOT on
44185 // a boundary
44186 if (!el_pt->node_pt(4)->is_on_boundary())
44187 {
44188 // Reset the internal nodes
44189 for (unsigned i = 0; i < 2; i++)
44190 {
44191 el_pt->node_pt(4)->x(i) =
44192 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
44193 }
44194 }
44195 // Make sure that the node I'm about to move is NOT on
44196 // a boundary
44197 if (!el_pt->node_pt(3)->is_on_boundary())
44198 {
44199 // Reset the internal nodes
44200 for (unsigned i = 0; i < 2; i++)
44201 {
44202 el_pt->node_pt(3)->x(i) =
44203 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
44204 }
44205 }
44206 }
44207
44208 // If it's seven noded it's likely to be an enriched one: Deal with
44209 // the central (bubble) node
44210 if (nnod == 7)
44211 {
44212 // Try to cast to an enriched quadratic element
44214 dynamic_cast<TBubbleEnrichedElement<2, 3>*>(el_pt);
44215 if (t_el_pt == 0)
44216 {
44217 throw OomphLibError("Have seven-noded element that's not a "
44218 "TBubbleEnrichedElement<2,3>",
44219 OOMPH_CURRENT_FUNCTION,
44220 OOMPH_EXCEPTION_LOCATION);
44221 }
44222
44223 // Assign the new non-bubble coordinates to the six noded dummy
44224 // element
44225 for (unsigned j = 0; j < 6; j++)
44226 {
44227 for (unsigned i = 0; i < 2; i++)
44228 {
44229 dummy_six_node_element.node_pt(j)->x(i) = el_pt->node_pt(j)->x(i);
44230 }
44231 }
44232
44233 // Local coordinate of enriched node
44234 unsigned j_enriched = 6;
44235 Vector<double> s(2);
44236 el_pt->local_coordinate_of_node(j_enriched, s);
44237
44238 // Get its position from non-enriched element
44239 Vector<double> x(2);
44240 dummy_six_node_element.interpolated_x(s, x);
44241 el_pt->node_pt(j_enriched)->x(0) = x[0];
44242 el_pt->node_pt(j_enriched)->x(1) = x[1];
44243 }
44244 }
44245 // Any other case cannot be dealt with at the moment
44246
44247 else
44248 {
44249 std::ostringstream error_stream;
44250 error_stream << "Cannot deal with this particular " << nnod
44251 << "-noded element yet.\n"
44252 << "Please implement this yourself.\n";
44253 throw OomphLibError(
44254 error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
44255 }
44256 }
44257
44258 // Cleanup
44259 for (unsigned j = 0; j < 6; j++)
44260 {
44261 delete dummy_six_node_element.node_pt(j);
44262 }
44263 }
44264
44265
44266#endif // #ifdef OOMPH_HAS_TRIANGLE_LIB
44267
44268} // namespace oomph
44269
44270#endif
e
Definition: cfortran.h:571
static char t char * s
Definition: cfortran.h:568
cstr elem_len * i
Definition: cfortran.h:603
char t
Definition: cfortran.h:568
CGAL-based SamplePointContainer.
NonRefineableBinArray class.
void get_bin(const Vector< double > &zeta, int &bin_number)
Get the number of the bin containing the specified coordinate. Bin number is negative if the coordina...
void fill_bin_by_diffusion(const unsigned &bin_diffusion_radius=1)
Fill bin by diffusion, populating each empty bin with the same content as the first non-empty bin fou...
const std::map< unsigned, Vector< std::pair< FiniteElement *, Vector< double > > > > * get_all_bins_content() const
Get the contents of all bins in vector.
void get_fill_stats(unsigned &n_bin, unsigned &max_n_entry, unsigned &min_n_entry, unsigned &tot_n_entry, unsigned &n_empty) const
Provide some stats on the fill level of the associated bin.
Algebraic meshes contain AlgebraicElements and AlgebraicNodes. They implement the node update functio...
virtual void update_node_update(AlgebraicNode *&node_pt)=0
Update the node update info for given node, following mesh adaptation. Must be implemented for every ...
GeomObject * geom_object_list_pt(const unsigned &i)
Access function to the ith GeomObject.
unsigned ngeom_object_list_pt()
Return number of geometric objects associated with AlgebraicMesh.
Algebraic nodes are nodes with an algebraic positional update function.
unsigned ngeom_object(const int &id)
Number of geometric objects involved in id-th update function.
unsigned nref_value(const int &id)
Number of reference values involved in id-th update function.
GeomObject * geom_object_pt(const unsigned &i)
Return pointer to i-th geometric object involved in default (usually first) update function.
int node_update_fct_id()
Default (usually first if there are multiple ones) node update fct id.
double ref_value(const unsigned &i)
Return i-th reference value involved in default (usually first) update function.
void add_node_update_info(const int &id, AlgebraicMesh *mesh_pt, const Vector< GeomObject * > &geom_object_pt, const Vector< double > &ref_value, const bool &called_from_constructor=false)
Add algebraic update information for node: What's the ID of the mesh update function (typically used ...
Vector< unsigned > & dimensions_of_bin_array()
Number of bins in each coordinate direction.
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition: nodes.h:1997
std::map< unsigned, unsigned > *& index_of_first_value_assigned_by_face_element_pt()
Return pointer to the map giving the index of the first face element value.
Definition: nodes.h:2047
Helper object for dealing with the parameters used for the CGALSamplePointContainer objects.
A class that represents a collection of data; each Data object may contain many different individual ...
Definition: nodes.h:86
void set_value(const unsigned &i, const double &value_)
Set the i-th stored data value to specified value. The only reason that we require an explicit set fu...
Definition: nodes.h:271
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:483
double value(const unsigned &i) const
Return i-th stored value. This function is not virtual so that it can be inlined. This means that if ...
Definition: nodes.h:293
Information for documentation of results: Directory and file number to enable output in the form RESL...
void disable_doc()
Disable documentation.
Dummy FaceElement for use with purely geometric operations such as mesh generation.
Definition: elements.h:5042
Class that is used to create FaceElement from bulk elements and to provide these FaceElement with a g...
void set_boundary_number_in_bulk_mesh(const unsigned &b)
Set function for the boundary number in bulk mesh.
Definition: elements.h:4510
A general Finite Element class.
Definition: elements.h:1313
virtual void local_coordinate_of_node(const unsigned &j, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size (broken virtual)
Definition: elements.h:1847
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition: elements.h:2514
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2215
virtual Node * construct_boundary_node(const unsigned &n)
Construct the local node n as a boundary node; that is a node that MAY be placed on a mesh boundary a...
Definition: elements.h:2543
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2180
A Generalised Element class.
Definition: elements.h:73
bool is_halo() const
Is this element a halo?
Definition: elements.h:1163
int non_halo_proc_ID()
ID of processor ID that holds non-halo counterpart of halo element; negative if not a halo.
Definition: elements.h:1170
bool must_be_kept_as_halo() const
Test whether the element must be kept as a halo element.
Definition: elements.h:1189
Generalised timestepper that can serve a variety of purposes in continuation, bifurcation detection a...
A geometric object is an object that provides a parametrised description of its shape via the functio...
Definition: geom_objects.h:101
unsigned ndim() const
Access function to # of Eulerian coordinates.
Definition: geom_objects.h:177
MacroElementNodeUpdateMeshes contain MacroElementNodeUpdateNodes which have their own node update fun...
Vector< GeomObject * > geom_object_vector_pt()
Access function to the vector of GeomObject.
MacroElementNodeUpdate nodes are nodes with a positional update function, based on their element's Ma...
void set_node_update_info(FiniteElement *node_update_element_pt, const Vector< double > &s_in_node_update_element, const Vector< GeomObject * > &geom_object_pt)
Set node update information for node: Pass the pointer to the element that performs the update operat...
This class provides a GeomObject representation of a given finite element mesh. The Lagrangian coordi...
SamplePointContainer * sample_point_container_pt() const
Pointer to the sample point container.
void position(const Vector< double > &zeta, Vector< double > &r) const
Return the position as a function of the intrinsic coordinate zeta. This provides an (expensive!...
A general mesh class.
Definition: mesh.h:67
unsigned long nboundary_node(const unsigned &ibound) const
Return number of nodes on a particular boundary.
Definition: mesh.h:833
void flush_element_and_node_storage()
Flush storage for elements and nodes by emptying the vectors that store the pointers to them....
Definition: mesh.h:407
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition: mesh.h:473
int face_index_at_boundary(const unsigned &b, const unsigned &e) const
For the e-th finite element on boundary b, return int to indicate the face_index of the face adjacent...
Definition: mesh.h:896
unsigned nboundary_element(const unsigned &b) const
Return number of finite elements that are adjacent to boundary b.
Definition: mesh.h:878
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition: mesh.h:436
void set_nodal_and_elemental_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Set the timestepper associated with all nodal and elemental data stored in the mesh.
Definition: mesh.h:1032
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition: mesh.h:840
unsigned nboundary() const
Return number of boundaries.
Definition: mesh.h:827
unsigned long nnode() const
Return number of nodes in the mesh.
Definition: mesh.h:596
GeneralisedElement *& element_pt(const unsigned long &e)
Return pointer to element e.
Definition: mesh.h:448
void add_element_pt(GeneralisedElement *const &element_pt)
Add a (pointer to) an element to the mesh.
Definition: mesh.h:617
void flush_node_storage()
Flush storage for nodes (only) by emptying the vectors that store the pointers to them.
Definition: mesh.h:430
unsigned long nelement() const
Return number of elements in the mesh.
Definition: mesh.h:590
Node *& boundary_node_pt(const unsigned &b, const unsigned &n)
Return pointer to node n on boundary b.
Definition: mesh.h:493
Helper object for dealing with the parameters used for the NonRefineableBinArray objects.
An oomph-lib wrapper to the MPI_Comm communicator object. Just contains an MPI_Comm object (which is ...
Definition: communicator.h:54
An OomphLibError object which should be thrown when an run-time error is encountered....
An OomphLibWarning object which should be created as a temporary object to issue a warning....
void enable_problem_distributed()
Enable problem distributed.
Definition: problem.h:971
void add_time_stepper_pt(TimeStepper *const &time_stepper_pt)
Add a timestepper to the problem. The function will automatically create or resize the Time object so...
Definition: problem.cc:1545
Mesh *& mesh_pt()
Return a pointer to the global mesh.
Definition: problem.h:1280
Projection problem. This is created during the adaptation of unstructured meshes and it is assumed th...
Definition: projection.h:695
void disable_use_iterative_solver_for_projection()
Disbales the use of an iterative solver for projection.
Definition: projection.h:742
void project(Mesh *base_mesh_pt, const bool &dont_project_positions=false)
Project from base into the problem's own mesh.
Definition: projection.h:748
Unstructured refineable Triangle Mesh upgraded to solid mesh.
Unstructured refineable Triangle Mesh.
void construct_new_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new halo node (on an element) with the information sent from the h...
void add_received_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add a new node from load balance.
bool update_shared_curve_using_elements_area(Vector< TriangleMeshPolyLine * > &vector_polyline_pt, const Vector< double > &target_areas)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
bool get_connected_vertex_number_on_dst_boundary(Vector< double > &vertex_coordinates, const unsigned &dst_b_id, unsigned &vertex_number)
Computes the associated vertex number on the destination boundary.
void send_boundary_node_info_of_shared_nodes(Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Get the original boundaries to which is associated each shared node, and send the info....
void update_shared_curve_after_restart(Vector< TriangleMeshPolyLine * > &vector_polyline_pt)
Updates the shared polylines representation after restart.
void send_and_receive_elements_nodes_info(int &send_proc, int &recv_proc)
Helper function to send back halo and haloed information.
void add_halo_node_helper(Node *&new_nod_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to add halo node.
double & max_element_size()
Max element size allowed during adaptation.
void snap_nodes_onto_boundary(RefineableTriangleMesh< ELEMENT > *&new_mesh_pt, const unsigned &b)
Snap the boundary nodes onto any curvilinear boundaries.
void reset_shared_boundary_elements_and_nodes(const bool flush_elements=true, const bool update_elements=true, const bool flush_nodes=true, const bool update_nodes=true)
Re-establish the shared boundary elements after the adaptation process (the updating of shared nodes ...
bool unrefine_shared_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void get_boundary_segment_nodes_helper(const unsigned &b, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the boundary (b), these are stored in the segment they belong (also used by the load...
bool unrefine_boundary(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, const bool &check_only=false)
Helper function that performs the unrefinement process.
void get_face_mesh_representation(TriangleMeshPolygon *polygon_pt, Vector< Mesh * > &face_mesh_pt)
Helper function to construct face mesh representation of all polylines, possibly with segments re-dis...
void add_non_delete_vertices_from_boundary_helper(Vector< Vector< Node * > > src_bound_segment_node_pt, Vector< Vector< Node * > > dst_bound_segment_node_pt, const unsigned &dst_bnd_id, const unsigned &dst_bnd_chunk)
Adds the vertices from the sources boundary that are repeated in the destination boundary to the list...
void add_node_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_halo_ele_pt, Vector< Node * > &new_nodes_on_domain, Node *nod_pt)
Helper function to add haloed node.
void compute_shared_node_degree_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, std::map< Node *, unsigned > &global_node_degree)
Computes the degree of the nodes on the shared boundaries, the degree of the node is computed from th...
void resume_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Resume the boundary connections that may have been suspended because the destination boundary is no p...
void get_required_elemental_information_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, FiniteElement *ele_pt)
Helper function to get the required elemental information from the element to be sent....
void sort_nodes_on_shared_boundaries()
Sort the nodes on shared boundaries so that the processors that share a boundary agree with the order...
double & min_element_size()
Min element size allowed during adaptation.
bool update_open_curve_using_elements_area(TriangleMeshOpenCurve *&open_curve_pt, const Vector< double > &target_area)
Updates the open curve but using the elements area instead of the default refinement and unrefinement...
void construct_new_node_load_balance_helper(Node *&new_nod_pt, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, unsigned &iproc, unsigned &node_index, FiniteElement *const &new_el_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function which constructs a new node (on an element) with the information sent from the load b...
void create_halo_element(unsigned &iproc, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void get_shared_boundary_elements_and_face_indexes(const Vector< FiniteElement * > &first_element_pt, const Vector< FiniteElement * > &second_element_pt, Vector< FiniteElement * > &first_shared_boundary_element_pt, Vector< unsigned > &first_shared_boundary_element_face_index, Vector< FiniteElement * > &second_shared_boundary_element_pt, Vector< unsigned > &second_shared_boundary_element_face_index)
Use the first and second group of elements to find the intersection between them to get the shared bo...
void create_sorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt, std::map< FiniteElement *, bool > &is_inverted, bool &inverted_face_mesh)
Helper function Creates a sorted face mesh representation of the specified PolyLine It means that the...
void update_open_curve_after_restart(TriangleMeshOpenCurve *&open_curve_pt)
Updates the open curve representation after restart.
void reset_halo_haloed_scheme()
In charge of. re-establish the halo(ed) scheme on all processors. Sends info. to create halo elements...
bool refine_boundary(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, const bool &check_only=false)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_element_load_balance_helper(unsigned &iproc, Vector< Vector< FiniteElement * > > &f_haloed_ele_pt, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, Vector< FiniteElement * > &new_elements_on_domain, Vector< Node * > &new_nodes_on_domain, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void create_new_shared_boundaries(std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< FiniteElement * > > &new_shared_boundary_element_pt, Vector< Vector< unsigned > > &new_shared_boundary_element_face_index)
Creates the new shared boundaries, this method is also in charge of computing the shared boundaries i...
void get_shared_boundary_segment_nodes_helper(const unsigned &shd_bnd_id, Vector< Vector< Node * > > &tmp_segment_nodes)
Get the nodes on the shared boundary (b), these are stored in the segment they belong.
void refine_triangulateio(TriangulateIO &triangulate_io, const Vector< double > &target_area, TriangulateIO &triangle_refine)
Build a new TriangulateIO object from previous TriangulateIO based on target area for each element.
void load_balance(const Vector< unsigned > &input_target_domain_for_local_non_halo_element)
Performs the load balancing for unstructured meshes, the load balancing strategy is based on mesh mig...
void create_unsorted_face_mesh_representation(const unsigned &boundary_id, Mesh *face_mesh_pt)
Helper function Creates an unsorted face mesh representation from the specified boundary id....
void add_element_load_balance_helper(const unsigned &iproc, Vector< Vector< std::map< unsigned, FiniteElement * > > > &received_old_haloed_element_pt, FiniteElement *ele_pt)
Helper function to create elements on the loop process based on the info received in send_and_receive...
void restore_boundary_connections(Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the boundaries ...
bool update_open_curve_using_face_mesh(TriangleMeshOpenCurve *open_polyline_pt, const bool &check_only=false)
Helper function that updates the input open curve by using end-points of elements from FaceMesh(es) t...
bool apply_max_length_constraint(Mesh *face_mesh_pt, Vector< Vector< double > > &vector_bnd_vertices, double &max_length_constraint)
bool refine_boundary_constrained_by_target_area(MeshAsGeomObject *mesh_geom_obj_pt, Vector< Vector< double > > &vector_bnd_vertices, double &refinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
void create_temporary_boundary_connections(Vector< TriangleMeshPolygon * > &tmp_outer_polygons_pt, Vector< TriangleMeshOpenCurve * > &tmp_open_curves_pt)
After unrefinement and refinement has taken place compute the new vertices numbers of the temporary r...
void add_haloed_node_helper(unsigned &iproc, Node *nod_pt)
Helper function to add haloed node.
void get_required_nodal_information_helper(unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from a haloed node so that a fully-functional h...
void compute_global_node_names_and_shared_nodes(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Compute the names of the nodes on shared boundaries in this (my_rank) processor with other processors...
double & min_permitted_angle()
Min angle before remesh gets triggered.
void reset_halo_haloed_scheme_helper(Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< Vector< Node * > > &iproc_currently_created_nodes_pt, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
In charge of creating additional halo(ed) elements on those processors that have no shared boundaries...
void get_required_nodal_information_load_balance_helper(Vector< Vector< FiniteElement * > > &f_halo_ele_pt, unsigned &iproc, Node *nod_pt)
Helper function to get the required nodal information from an haloed node so that a fully-functional ...
bool unrefine_boundary_constrained_by_target_area(const unsigned &b, const unsigned &c, Vector< Vector< double > > &vector_bnd_vertices, double &unrefinement_tolerance, Vector< double > &area_constraint)
Helper function that performs the unrefinement process on the specified boundary by using the provide...
void add_halo_element_helper(unsigned &iproc, FiniteElement *ele_pt)
Helper function to create (halo) elements on the loop process based on the info received in send_and_...
void update_other_proc_shd_bnd_node_helper(Node *&new_nod_pt, Vector< Vector< Vector< std::map< unsigned, Node * > > > > &other_proc_shd_bnd_node_pt, Vector< unsigned > &other_processor_1, Vector< unsigned > &other_processor_2, Vector< unsigned > &other_shared_boundaries, Vector< unsigned > &other_indexes, Vector< Vector< Vector< unsigned > > > &global_node_names, std::map< Vector< unsigned >, unsigned > &node_name_to_global_index, Vector< Node * > &global_shared_node_pt)
Helper function that assigns/updates the references to the node so that it can be found with any othe...
void create_adjacency_matrix_new_shared_edges_helper(Vector< Vector< FiniteElement * > > &unsorted_face_ele_pt, Vector< Vector< Node * > > &tmp_sorted_shared_node_pt, std::map< Node *, Vector< Vector< unsigned > > > &node_alias, Vector< Vector< Vector< unsigned > > > &adjacency_matrix)
Sort the nodes on the new shared boundaries (after load balancing), computes the alias of the nodes a...
bool refine_shared_boundary_constrained_by_target_area(Vector< Vector< double > > &vector_bnd_vertices, Vector< double > &area_constraint)
Helper function that performs the refinement process on the specified boundary by using the provided ...
bool update_polygon_using_face_mesh(TriangleMeshPolygon *polygon_pt, const bool &check_only=false)
Helper function that updates the input polygon's PSLG by using the end-points of elements from FaceMe...
void update_polygon_after_restart(TriangleMeshPolygon *&polygon_pt)
Updates the polylines representation after restart.
void add_vertices_for_non_deletion()
Mark the vertices that are not allowed for deletion by the unrefienment/refinement polyline methods....
void create_polylines_from_polyfiles(const std::string &node_file_name, const std::string &poly_file_name)
Helper function to create polylines and fill associate data.
bool update_polygon_using_elements_area(TriangleMeshPolygon *&polygon_pt, const Vector< double > &target_area)
Updates the polylines using the elements area as constraint for the number of points along the bounda...
void restore_polyline_connections_helper(TriangleMeshPolyLine *polyline_pt, Vector< TriangleMeshPolyLine * > &resume_initial_connection_polyline_pt, Vector< TriangleMeshPolyLine * > &resume_final_connection_polyline_pt)
Restore the connections of the specific polyline The vertices numbering on the destination boundaries...
void adapt(const Vector< double > &elem_error)
Adapt mesh, based on elemental error provided.
void enable_use_eulerian_coordinates_during_setup()
Enable use of eulerian coordinates (via interpolated_x) during setup (otherwise use interpolated_zeta...
General SolidMesh class.
Definition: mesh.h:2562
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition: nodes.h:1687
void add_values_to_vector(Vector< double > &vector_of_values)
Add all data, position and time history values to the vector Overload to add the Lagrangian coordinat...
Definition: nodes.cc:3708
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1766
void read_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all data and time history values from the vector starting from index. On return the index will b...
Definition: nodes.cc:3755
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:231
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Definition: timesteppers.h:601
Class to keep track of discrete/continous time. It is essential to have a single Time object when usi...
Definition: timesteppers.h:63
TriangulateIO & triangulateio_representation()
Access to the triangulateio representation of the mesh.
bool is_internal_point_fixed() const
Test whether the internal point is fixed.
Vector< double > internal_point() const
Coordinates of the internal point.
Base class for defining a triangle mesh boundary, this class has the methods that allow to connect th...
virtual unsigned boundary_chunk() const =0
Boundary chunk (Used when a boundary is represented by more than one polyline.
double unrefinement_tolerance()
Get tolerance for unrefinement of curve section to create a better representation of curvilinear boun...
void set_unrefinement_tolerance(const double &tolerance)
Set tolerance for unrefinement of curve sections to avoid unnecessarily large numbers of elements on ...
virtual unsigned boundary_id() const =0
Boundary id.
void connect_initial_vertex_to_polyline(TriangleMeshPolyLine *polyline_pt, const unsigned &vertex_number, const double &tolerance_for_connection=1.0e-14)
Connects the initial vertex of the curve section to a desired target polyline by specifying the verte...
void resume_initial_vertex_connected()
Resumes the initial vertex connection, it may be that after load balancing the boundary to which the ...
void suspend_final_vertex_connected()
Set the final vertex connection as suspended, it will be resumed when the method to resume the connec...
unsigned initial_vertex_connected_n_vertex() const
Gets the vertex number to which the initial end is connected.
void resume_final_vertex_connected()
Resumes the final vertex connection, it may be that after load balancing the boundary to which the co...
void set_refinement_tolerance(const double &tolerance)
Set tolerance for refinement of curve sections to create a better representation of curvilinear bound...
virtual unsigned nvertex() const =0
Number of vertices.
void connect_final_vertex_to_polyline(TriangleMeshPolyLine *polyline_pt, const unsigned &vertex_number, const double &tolerance_for_connection=1.0e-14)
Connects the final vertex of the curve section to a desired target polyline by specifying the vertex ...
unsigned final_vertex_connected_n_chunk() const
Gets the boundary chunk to which the final end is connected.
void set_maximum_length(const double &maximum_length)
Allows to specify the maximum distance between two vertices that define the associated polyline of th...
unsigned final_vertex_connected_n_vertex() const
Sets the vertex number to which the final end is connected.
double refinement_tolerance()
Get tolerance for refinement of curve sections to create a better representation of curvilinear bound...
bool is_final_vertex_connected() const
Test whether final vertex is connected or not.
unsigned initial_vertex_connected_bnd_id() const
Gets the id to which the initial end is connected.
bool is_initial_vertex_connected() const
Test whether initial vertex is connected or not.
void set_initial_vertex_connected()
Sets the initial vertex as connected.
void set_final_vertex_connected()
Sets the final vertex as connected.
void suspend_initial_vertex_connected()
Set the initial vertex connection as suspended, it will be resumed when the method to resume the conn...
unsigned initial_vertex_connected_n_chunk() const
Gets the boundary chunk to which the initial end is connected.
double maximum_length()
Gets access to the maximum length variable.
unsigned final_vertex_connected_bnd_id() const
Gets the id to which the final end is connected.
virtual TriangleMeshCurveSection * curve_section_pt(const unsigned &i) const
Pointer to i-th constituent curve section.
virtual unsigned ncurve_section() const
Number of constituent curves.
Base class defining an open curve for the Triangle mesh generation Basically used to define internal ...
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
Helper object for dealing with the parameters used for the TriangleMesh objects.
void enable_use_attributes()
Helper function for enabling the use of attributes.
double element_area() const
Helper function for getting the element area.
void disable_automatic_creation_of_vertices_on_boundaries()
Disables the creation of points (by Triangle) on the outer and internal boundaries.
std::map< unsigned, Vector< double > > & regions_coordinates()
Helper function for getting access to the regions coordinates.
Vector< TriangleMeshClosedCurve * > internal_closed_curve_pt() const
Helper function for getting the internal closed boundaries.
Vector< Vector< double > > extra_holes_coordinates() const
Helper function for getting the extra holes.
void set_communicator_pt(OomphCommunicator *comm_pt)
Function to set communicator (mesh is then assumed to be distributed)
Vector< TriangleMeshOpenCurve * > internal_open_curves_pt() const
Helper function for getting the internal open boundaries.
Class defining a polyline for use in Triangle Mesh generation.
Vector< double > vertex_coordinate(const unsigned &i) const
Coordinate vector of i-th vertex (const version)
unsigned boundary_chunk() const
Boundary chunk (Used when a boundary is represented by more than one polyline.
void final_vertex_coordinate(Vector< double > &vertex)
Get last vertex coordinates.
unsigned nvertex() const
Number of vertices.
void reverse()
Reverse the polyline, this includes the connection information and the vertices order.
void initial_vertex_coordinate(Vector< double > &vertex)
Get first vertex coordinates.
Class defining a closed polygon for the Triangle mesh generation.
unsigned npolyline() const
Number of constituent polylines.
bool can_update_reference_configuration() const
Test whether curve can update reference.
TriangleMeshPolyLine * polyline_pt(const unsigned &i) const
Pointer to i-th constituent polyline.
bool is_redistribution_of_segments_between_polylines_enabled()
Is re-distribution of polyline segments in the curve between different boundaries during adaptation e...
virtual void reset_reference_configuration()
Virtual function that should be overloaded to update the polygons reference configuration.
Triangle mesh build with the help of the scaffold mesh coming from the triangle mesh generator Triang...
const int check_connections_of_polyline_nodes(std::set< FiniteElement * > &element_in_processor_pt, const int &root_edge_bnd_id, std::map< std::pair< Node *, Node * >, bool > &overlapped_face, std::map< unsigned, std::map< Node *, bool > > &node_on_bnd_not_overlapped_by_shd_bnd, std::list< Node * > &current_polyline_nodes, std::map< unsigned, std::list< Node * > > &shared_bnd_id_to_sorted_list_node_pt, const unsigned &node_degree, Node *&new_node_pt, const bool called_from_load_balance=false)
Check for any possible connections that the array of sorted nodes have with any previous boundaries o...
void compute_boundary_segments_connectivity_and_initial_zeta_values(const unsigned &b)
Compute the boundary segments connectivity for those boundaries that were splited during the distribu...
void break_loops_on_shared_polyline_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void build_triangulateio(const std::string &poly_file_name, TriangulateIO &triangulate_io, bool &use_attributes)
Helper function to create TriangulateIO object (return in triangulate_io) from the ....
void create_shared_boundaries(OomphCommunicator *comm_pt, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, const Vector< FiniteElement * > &backed_up_f_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status)
Creates the shared boundaries.
Vector< unsigned > oomph_vertex_nodes_id()
Return the vector that contains the oomph-lib node number for all vertex nodes in the TriangulateIO r...
void create_shared_polylines_connections()
Establish the connections of the polylines previously marked as having connections....
void break_loops_on_shared_polyline_load_balance_helper(const unsigned &initial_shd_bnd_id, std::list< Node * > &input_nodes, Vector< FiniteElement * > &input_boundary_element_pt, Vector< FiniteElement * > &input_boundary_face_element_pt, Vector< int > &input_face_index_element, const int &input_connect_to_the_left, const int &input_connect_to_the_right, Vector< std::list< Node * > > &output_sorted_nodes_pt, Vector< Vector< FiniteElement * > > &output_boundary_element_pt, Vector< Vector< FiniteElement * > > &output_boundary_face_element_pt, Vector< Vector< int > > &output_face_index_element, Vector< int > &output_connect_to_the_left, Vector< int > &output_connect_to_the_right)
Break any possible loop created by the sorted list of nodes that is used to create a new shared polyl...
void synchronize_boundary_coordinates(const unsigned &b)
In charge of sinchronize the boundary coordinates for internal boundaries that were split as part of ...
void compute_holes_left_by_halo_elements_helper(Vector< Vector< double > > &output_holes_coordinates)
Compute the holes left by the halo elements, those adjacent to the shared boundaries.
void identify_boundary_segments_and_assign_initial_zeta_values(const unsigned &b, Vector< FiniteElement * > &input_face_ele_pt, const bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Identify the segments from the old mesh (original mesh) in the new mesh (this) and assign initial and...
virtual void reset_boundary_element_info(Vector< unsigned > &ntmp_boundary_elements, Vector< Vector< unsigned > > &ntmp_boundary_elements_in_region, Vector< FiniteElement * > &deleted_elements)
Virtual function to perform the reset boundary elements info routines. Generally used after load bala...
const bool shared_boundary_overlaps_internal_boundary(const unsigned &shd_bnd_id)
Checks if the shared boundary overlaps an internal boundary.
Vector< Vector< Node * > > & boundary_segment_node_pt(const unsigned &b)
Return direct access to nodes associated with a boundary but sorted in segments.
Vector< Vector< Vector< unsigned > > > shared_boundaries_ids() const
void re_scale_re_assigned_initial_zeta_values_for_internal_boundary(const unsigned &b)
Re-scale the re-assigned zeta values for the boundary nodes, apply only for internal boundaries.
void create_shared_polyline(const unsigned &my_rank, const unsigned &shd_bnd_id, const unsigned &iproc, const unsigned &jproc, std::list< Node * > &sorted_nodes, const int &root_edge_bnd_id, Vector< FiniteElement * > &bulk_bnd_ele_pt, Vector< int > &face_index_ele, Vector< Vector< TriangleMeshPolyLine * > > &unsorted_polylines_pt, const int &connect_to_the_left_flag, const int &connect_to_the_right_flag)
Create the shared polyline and fill the data structured that keep all the information associated with...
void update_holes_information_helper(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< Vector< double > > &output_holes_coordinates)
Keeps those vertices that define a hole, those that are inside closed internal boundaries in the new ...
void create_distributed_domain_representation(Vector< TriangleMeshPolygon * > &polygons_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Creates the distributed domain representation. Joins the original boundaires, shared boundaries and c...
void build_from_scaffold(TimeStepper *time_stepper_pt, const bool &use_attributes)
Build mesh from scaffold.
void sort_polylines_helper(Vector< TriangleMeshPolyLine * > &unsorted_polylines_pt, Vector< Vector< TriangleMeshPolyLine * > > &sorted_polylines_pt)
Sorts the polylines so they be continuous and then we can create a closed or open curve from them.
void create_tmp_open_curves_helper(Vector< Vector< TriangleMeshPolyLine * > > &sorted_open_curves_pt, Vector< TriangleMeshPolyLine * > &unsorted_shared_to_internal_poly_pt, Vector< TriangleMeshOpenCurve * > &open_curves_pt)
Take the polylines from the original open curves and created new temporaly representations of open cu...
void get_halo_elements_on_all_procs(const unsigned &nproc, const Vector< unsigned > &element_domain, const Vector< GeneralisedElement * > &backed_up_el_pt, std::map< Data *, std::set< unsigned > > &processors_associated_with_data, const bool &overrule_keep_as_halo_element_status, std::map< GeneralisedElement *, unsigned > &element_to_global_index, Vector< Vector< Vector< GeneralisedElement * > > > &output_halo_elements_pt)
Creates the halo elements on all processors Gets the halo elements on all processors,...
void dump_distributed_info_for_restart(std::ostream &dump_file)
Used to dump info. related with distributed triangle meshes.
void re_assign_initial_zeta_values_for_internal_boundary(const unsigned &b, Vector< std::list< FiniteElement * > > &old_segment_sorted_ele_pt, std::map< FiniteElement *, bool > &old_is_inverted)
Re-assign the boundary segments initial zeta (arclength) value for those internal boundaries that wer...
void set_mesh_level_time_stepper(TimeStepper *const &time_stepper_pt, const bool &preserve_existing_data)
Overload set_mesh_level_time_stepper so that the stored time stepper now corresponds to the new times...
void read_distributed_info_for_restart(std::istream &restart_file)
Used to read info. related with distributed triangle meshes.
void select_boundary_face_elements(Vector< FiniteElement * > &face_el_pt, const unsigned &b, bool &is_internal_boundary, std::map< FiniteElement *, FiniteElement * > &face_to_bulk_element_pt)
Select face element from boundary using the criteria to decide which of the two face elements should ...
void create_polylines_from_halo_elements_helper(const Vector< unsigned > &element_domain, std::map< GeneralisedElement *, unsigned > &element_to_global_index, std::set< FiniteElement * > &element_in_processor_pt, Vector< Vector< Vector< GeneralisedElement * > > > &input_halo_elements, std::map< std::pair< Node *, Node * >, unsigned > &elements_edges_on_boundary, Vector< Vector< Vector< TriangleMeshPolyLine * > > > &output_polylines_pt)
Creates polylines from the intersection of halo elements on all processors. The new polylines define ...
void output_boundary_coordinates(const unsigned &b, std::ostream &outfile)
Output the nodes on the boundary and their respective boundary coordinates(into separate tecplot zone...
void create_tmp_polygons_helper(Vector< Vector< TriangleMeshPolyLine * > > &polylines_pt, Vector< TriangleMeshPolygon * > &polygons_pt)
Take the polylines from the shared boundaries and create temporary polygon representations of the dom...
void get_element_edges_on_boundary(std::map< std::pair< Node *, Node * >, unsigned > &element_edges_on_boundary)
Get the element edges (pair of nodes, edges) that lie on a boundary (used to mark shared boundaries t...
std::map< unsigned, Vector< double > > & boundary_segment_final_arclength()
Return direct access to the final arclength for the segments that are part of a boundary.
std::map< unsigned, Vector< double > > & boundary_segment_initial_zeta()
Return direct access to the initial zeta for the segments that are part of a boundary.
GeomObject * boundary_geom_object_pt(const unsigned &b)
Return the geometric object associated with the b-th boundary or null if the boundary has associated ...
double region_attribute(const unsigned &i)
Return the attribute associated with region i.
FiniteElement * boundary_element_in_region_pt(const unsigned &b, const unsigned &r, const unsigned &e) const
Return pointer to the e-th element adjacent to boundary b in region r.
std::map< unsigned, Vector< double > > & boundary_final_coordinate()
Return direct access to the final coordinates of a boundary.
unsigned nboundary_segment(const unsigned &b)
Return the number of segments associated with a boundary.
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_final_coordinate()
Return direct access to the final coordinates for the segments that are part of a boundary.
unsigned long nboundary_segment_node(const unsigned &b)
Return the number of segments associated with a boundary.
std::map< unsigned, Vector< Vector< double > > > & boundary_segment_initial_coordinate()
Return direct access to the initial coordinates for the segments that are part of a boundary.
int face_index_at_boundary_in_region(const unsigned &b, const unsigned &r, const unsigned &e) const
Return face index of the e-th element adjacent to boundary b in region r.
std::map< unsigned, Vector< double > > & boundary_initial_coordinate()
Return direct access to the initial coordinates of a boundary.
unsigned nboundary_element_in_region(const unsigned &b, const unsigned &r) const
Return the number of elements adjacent to boundary b in region r.
unsigned nregion()
Return the number of regions specified by attributes.
std::map< unsigned, Vector< double > > & boundary_segment_final_zeta()
Return direct access to the final zeta for the segments that are part of a boundary.
std::map< unsigned, Vector< double > > & boundary_segment_initial_arclength()
Return direct access to the initial arclength for the segments that are part of a boundary.
FiniteElement * region_element_pt(const unsigned &i, const unsigned &e)
Return the e-th element in the i-th region.
std::map< unsigned, Vector< double > > & boundary_initial_zeta_coordinate()
Return direct access to the initial zeta coordinate of a boundary.
unsigned nregion_element(const unsigned &i)
Return the number of elements in the i-th region.
std::map< unsigned, Vector< double > > & boundary_coordinate_limits()
Return access to the vector of boundary coordinates associated with each geometric object.
std::map< unsigned, Vector< double > > & boundary_final_zeta_coordinate()
Return direct access to the final zeta coordinates of a boundary.
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition: Vector.h:58
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn't been defined.
bool Doc_comprehensive_timings
Global boolean to switch on comprehensive timing – can probably be declared const false when developm...
void get_required_nodal_information_helper(int &iproc, Node *nod_pt, Mesh *const &mesh_pt, int &n_cont_inter_values, Vector< unsigned > &send_unsigneds, Vector< double > &send_doubles)
Helper function to get the required nodal information from an external haloed node so that a fully-fu...
Vector< std::string > Flat_packed_unsigneds_string
unsigned Counter_for_flat_packed_unsigneds
Counter used when processing vector of flat-packed unsigneds – this is really "private" data,...
Vector< double > Flat_packed_doubles
Vector of flat-packed doubles to be communicated with other processors.
Vector< unsigned > Flat_packed_unsigneds
Vector of flat-packed unsigneds to be communicated with other processors – this is really "private" d...
bool Doc_timings
Boolean to indicate whether to doc timings or not.
bool Doc_stats
Boolean to indicate whether to output basic info during setup_multi_domain_interaction() routines.
bool Doc_full_stats
Boolean to indicate whether to output further info during setup_multi_domain_interaction() routines.
unsigned Counter_for_flat_packed_doubles
Counter used when processing vector of flat-packed doubles – this is really "private" data,...
double timer()
returns the time in seconds after some point in past
double Tolerable_error
Acceptable discrepancy for mismatch in vertex coordinates. In paranoid mode, the code will die if the...
void initialise_triangulateio(TriangulateIO &triangle_io)
Initialise TriangulateIO structure.
void clear_triangulateio(TriangulateIO &triangulate_io, const bool &clear_hole_data)
Clear TriangulateIO structure.
TriangulateIO deep_copy_of_triangulateio_representation(TriangulateIO &triangle_io, const bool &quiet)
Make (partial) deep copy of TriangulateIO object. We only copy those items we need within oomph-lib's...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
frac
A class for all elements that solve the Advection Diffusion equations using isoparametric elements.
x
Pseudo buckling ring: Circular ring deformed by the N-th buckling mode of a thin-wall elastic ring.
u
Z2-error-estimator: Elements that can be used with Z2 error estimation should be derived from the bas...
struct oomph::classcomp Bottom_left_sorter
int f(x_0, x_1...)\ dx_0 \ dx_1...
Generic class for numerical integration schemes:
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...
The Triangle data structure, modified from the triangle.h header supplied with triangle 1....
double * pointlist
Pointer to list of points x coordinate followed by y coordinate.
int * pointmarkerlist
Pointer to list of point markers.
double * pointattributelist
Pointer to list of point attributes.
bool operator()(const std::pair< double, double > &lhs, const std::pair< double, double > &rhs) const